@norskvideo/moq-net 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +1 -0
  2. package/announced.d.ts +111 -21
  3. package/announced.d.ts.map +1 -1
  4. package/announced.js +213 -40
  5. package/announced.js.map +1 -1
  6. package/broadcast.d.ts +80 -25
  7. package/broadcast.d.ts.map +1 -1
  8. package/broadcast.js +270 -49
  9. package/broadcast.js.map +1 -1
  10. package/connection/accept.d.ts +6 -0
  11. package/connection/accept.d.ts.map +1 -1
  12. package/connection/accept.js +42 -13
  13. package/connection/accept.js.map +1 -1
  14. package/connection/browser.d.ts +5 -0
  15. package/connection/browser.d.ts.map +1 -0
  16. package/connection/browser.js +31 -0
  17. package/connection/browser.js.map +1 -0
  18. package/connection/connect.d.ts +43 -1
  19. package/connection/connect.d.ts.map +1 -1
  20. package/connection/connect.js +118 -123
  21. package/connection/connect.js.map +1 -1
  22. package/connection/established.d.ts +43 -16
  23. package/connection/established.d.ts.map +1 -1
  24. package/connection/established.js.map +1 -1
  25. package/connection/handshake.d.ts +12 -1
  26. package/connection/handshake.d.ts.map +1 -1
  27. package/connection/handshake.js +30 -14
  28. package/connection/handshake.js.map +1 -1
  29. package/connection/index.d.ts +8 -0
  30. package/connection/index.d.ts.map +1 -1
  31. package/connection/index.js +6 -0
  32. package/connection/index.js.map +1 -1
  33. package/connection/reload.d.ts +66 -9
  34. package/connection/reload.d.ts.map +1 -1
  35. package/connection/reload.js +192 -66
  36. package/connection/reload.js.map +1 -1
  37. package/connection/stats.d.ts +92 -0
  38. package/connection/stats.d.ts.map +1 -0
  39. package/connection/stats.js +35 -0
  40. package/connection/stats.js.map +1 -0
  41. package/connection/transport.d.ts +10 -0
  42. package/connection/transport.d.ts.map +1 -0
  43. package/connection/transport.js +12 -0
  44. package/connection/transport.js.map +1 -0
  45. package/consume.d.ts +44 -0
  46. package/consume.d.ts.map +1 -0
  47. package/consume.js +60 -0
  48. package/consume.js.map +1 -0
  49. package/datagram.d.ts +23 -0
  50. package/datagram.d.ts.map +1 -0
  51. package/datagram.js +3 -0
  52. package/datagram.js.map +1 -0
  53. package/error.d.ts +76 -0
  54. package/error.d.ts.map +1 -0
  55. package/error.js +107 -0
  56. package/error.js.map +1 -0
  57. package/group.d.ts +143 -38
  58. package/group.d.ts.map +1 -1
  59. package/group.js +270 -68
  60. package/group.js.map +1 -1
  61. package/ietf/adapter.d.ts +2 -2
  62. package/ietf/adapter.d.ts.map +1 -1
  63. package/ietf/adapter.js +20 -10
  64. package/ietf/adapter.js.map +1 -1
  65. package/ietf/aliases.d.ts +73 -5
  66. package/ietf/aliases.d.ts.map +1 -1
  67. package/ietf/aliases.js +121 -14
  68. package/ietf/aliases.js.map +1 -1
  69. package/ietf/cluster.d.ts +141 -0
  70. package/ietf/cluster.d.ts.map +1 -0
  71. package/ietf/cluster.js +204 -0
  72. package/ietf/cluster.js.map +1 -0
  73. package/ietf/connection.d.ts +39 -6
  74. package/ietf/connection.d.ts.map +1 -1
  75. package/ietf/connection.js +73 -12
  76. package/ietf/connection.js.map +1 -1
  77. package/ietf/fetch.d.ts +17 -0
  78. package/ietf/fetch.d.ts.map +1 -1
  79. package/ietf/fetch.js +21 -0
  80. package/ietf/fetch.js.map +1 -1
  81. package/ietf/filter.d.ts +87 -0
  82. package/ietf/filter.d.ts.map +1 -0
  83. package/ietf/filter.js +292 -0
  84. package/ietf/filter.js.map +1 -0
  85. package/ietf/index.d.ts +2 -0
  86. package/ietf/index.d.ts.map +1 -1
  87. package/ietf/index.js +3 -0
  88. package/ietf/index.js.map +1 -1
  89. package/ietf/namespace.d.ts +6 -0
  90. package/ietf/namespace.d.ts.map +1 -1
  91. package/ietf/namespace.js +27 -2
  92. package/ietf/namespace.js.map +1 -1
  93. package/ietf/object.d.ts +54 -6
  94. package/ietf/object.d.ts.map +1 -1
  95. package/ietf/object.js +178 -25
  96. package/ietf/object.js.map +1 -1
  97. package/ietf/parameters.d.ts +35 -10
  98. package/ietf/parameters.d.ts.map +1 -1
  99. package/ietf/parameters.js +236 -45
  100. package/ietf/parameters.js.map +1 -1
  101. package/ietf/priority.d.ts +5 -0
  102. package/ietf/priority.d.ts.map +1 -0
  103. package/ietf/priority.js +10 -0
  104. package/ietf/priority.js.map +1 -0
  105. package/ietf/properties.d.ts +13 -2
  106. package/ietf/properties.d.ts.map +1 -1
  107. package/ietf/properties.js +65 -8
  108. package/ietf/properties.js.map +1 -1
  109. package/ietf/publish.d.ts.map +1 -1
  110. package/ietf/publish.js +13 -3
  111. package/ietf/publish.js.map +1 -1
  112. package/ietf/publish_namespace.d.ts +16 -2
  113. package/ietf/publish_namespace.d.ts.map +1 -1
  114. package/ietf/publish_namespace.js +25 -6
  115. package/ietf/publish_namespace.js.map +1 -1
  116. package/ietf/publisher.d.ts +40 -7
  117. package/ietf/publisher.d.ts.map +1 -1
  118. package/ietf/publisher.js +751 -134
  119. package/ietf/publisher.js.map +1 -1
  120. package/ietf/request.js +1 -1
  121. package/ietf/request.js.map +1 -1
  122. package/ietf/solicit.d.ts +41 -0
  123. package/ietf/solicit.d.ts.map +1 -0
  124. package/ietf/solicit.js +47 -0
  125. package/ietf/solicit.js.map +1 -0
  126. package/ietf/subscribe.d.ts +41 -2
  127. package/ietf/subscribe.d.ts.map +1 -1
  128. package/ietf/subscribe.js +103 -22
  129. package/ietf/subscribe.js.map +1 -1
  130. package/ietf/subscribe_namespace.d.ts +21 -7
  131. package/ietf/subscribe_namespace.d.ts.map +1 -1
  132. package/ietf/subscribe_namespace.js +31 -14
  133. package/ietf/subscribe_namespace.js.map +1 -1
  134. package/ietf/subscriber.d.ts +20 -7
  135. package/ietf/subscriber.d.ts.map +1 -1
  136. package/ietf/subscriber.js +512 -142
  137. package/ietf/subscriber.js.map +1 -1
  138. package/ietf/version.d.ts +7 -1
  139. package/ietf/version.d.ts.map +1 -1
  140. package/ietf/version.js +7 -0
  141. package/ietf/version.js.map +1 -1
  142. package/index.d.ts +10 -5
  143. package/index.d.ts.map +1 -1
  144. package/index.js +10 -5
  145. package/index.js.map +1 -1
  146. package/internal.d.ts +29 -0
  147. package/internal.d.ts.map +1 -0
  148. package/internal.js +8 -0
  149. package/internal.js.map +1 -0
  150. package/lite/announce.d.ts +62 -15
  151. package/lite/announce.d.ts.map +1 -1
  152. package/lite/announce.js +230 -94
  153. package/lite/announce.js.map +1 -1
  154. package/lite/connection.d.ts +69 -20
  155. package/lite/connection.d.ts.map +1 -1
  156. package/lite/connection.js +128 -81
  157. package/lite/connection.js.map +1 -1
  158. package/lite/datagram.d.ts +23 -0
  159. package/lite/datagram.d.ts.map +1 -0
  160. package/lite/datagram.js +57 -0
  161. package/lite/datagram.js.map +1 -0
  162. package/lite/datagram_stream.d.ts +8 -0
  163. package/lite/datagram_stream.d.ts.map +1 -0
  164. package/lite/datagram_stream.js +44 -0
  165. package/lite/datagram_stream.js.map +1 -0
  166. package/lite/fetch.d.ts.map +1 -1
  167. package/lite/fetch.js +2 -2
  168. package/lite/fetch.js.map +1 -1
  169. package/lite/index.d.ts +3 -0
  170. package/lite/index.d.ts.map +1 -1
  171. package/lite/index.js +3 -0
  172. package/lite/index.js.map +1 -1
  173. package/lite/priority.d.ts +67 -0
  174. package/lite/priority.d.ts.map +1 -0
  175. package/lite/priority.js +114 -0
  176. package/lite/priority.js.map +1 -0
  177. package/lite/probe.d.ts +18 -2
  178. package/lite/probe.d.ts.map +1 -1
  179. package/lite/probe.js +24 -22
  180. package/lite/probe.js.map +1 -1
  181. package/lite/publisher.d.ts +14 -7
  182. package/lite/publisher.d.ts.map +1 -1
  183. package/lite/publisher.js +406 -84
  184. package/lite/publisher.js.map +1 -1
  185. package/lite/setup.d.ts +83 -6
  186. package/lite/setup.d.ts.map +1 -1
  187. package/lite/setup.js +203 -31
  188. package/lite/setup.js.map +1 -1
  189. package/lite/stream.d.ts +12 -3
  190. package/lite/stream.d.ts.map +1 -1
  191. package/lite/stream.js +7 -4
  192. package/lite/stream.js.map +1 -1
  193. package/lite/subscribe.d.ts +37 -23
  194. package/lite/subscribe.d.ts.map +1 -1
  195. package/lite/subscribe.js +70 -55
  196. package/lite/subscribe.js.map +1 -1
  197. package/lite/subscriber.d.ts +29 -23
  198. package/lite/subscriber.d.ts.map +1 -1
  199. package/lite/subscriber.js +689 -112
  200. package/lite/subscriber.js.map +1 -1
  201. package/lite/track.d.ts +25 -13
  202. package/lite/track.d.ts.map +1 -1
  203. package/lite/track.js +60 -35
  204. package/lite/track.js.map +1 -1
  205. package/lite/version.d.ts +39 -4
  206. package/lite/version.d.ts.map +1 -1
  207. package/lite/version.js +139 -17
  208. package/lite/version.js.map +1 -1
  209. package/mock.d.ts +53 -3
  210. package/mock.d.ts.map +1 -1
  211. package/mock.js +110 -21
  212. package/mock.js.map +1 -1
  213. package/{lite/origin.d.ts → origin.d.ts} +25 -1
  214. package/origin.d.ts.map +1 -0
  215. package/{lite/origin.js → origin.js} +25 -1
  216. package/origin.js.map +1 -0
  217. package/package.json +6 -4
  218. package/path.d.ts +67 -0
  219. package/path.d.ts.map +1 -1
  220. package/path.js +128 -0
  221. package/path.js.map +1 -1
  222. package/stream.d.ts +94 -4
  223. package/stream.d.ts.map +1 -1
  224. package/stream.js +128 -17
  225. package/stream.js.map +1 -1
  226. package/time.d.ts +109 -86
  227. package/time.d.ts.map +1 -1
  228. package/time.js +80 -30
  229. package/time.js.map +1 -1
  230. package/track.d.ts +298 -47
  231. package/track.d.ts.map +1 -1
  232. package/track.js +721 -109
  233. package/track.js.map +1 -1
  234. package/util/timeout.d.ts +5 -0
  235. package/util/timeout.d.ts.map +1 -0
  236. package/util/timeout.js +18 -0
  237. package/util/timeout.js.map +1 -0
  238. package/util/utf8.d.ts +3 -0
  239. package/util/utf8.d.ts.map +1 -0
  240. package/util/utf8.js +7 -0
  241. package/util/utf8.js.map +1 -0
  242. package/varint.d.ts +14 -3
  243. package/varint.d.ts.map +1 -1
  244. package/varint.js +23 -12
  245. package/varint.js.map +1 -1
  246. package/zod.d.ts +5 -5
  247. package/zod.d.ts.map +1 -1
  248. package/zod.js.map +1 -1
  249. package/bandwidth.d.ts +0 -11
  250. package/bandwidth.d.ts.map +0 -1
  251. package/bandwidth.js +0 -7
  252. package/bandwidth.js.map +0 -1
  253. package/connection/cert.test.d.ts +0 -2
  254. package/connection/cert.test.d.ts.map +0 -1
  255. package/connection/cert.test.js +0 -26
  256. package/connection/cert.test.js.map +0 -1
  257. package/connection/reload.test.d.ts +0 -2
  258. package/connection/reload.test.d.ts.map +0 -1
  259. package/connection/reload.test.js +0 -39
  260. package/connection/reload.test.js.map +0 -1
  261. package/group.test.d.ts +0 -2
  262. package/group.test.d.ts.map +0 -1
  263. package/group.test.js +0 -66
  264. package/group.test.js.map +0 -1
  265. package/ietf/aliases.test.d.ts +0 -2
  266. package/ietf/aliases.test.d.ts.map +0 -1
  267. package/ietf/aliases.test.js +0 -30
  268. package/ietf/aliases.test.js.map +0 -1
  269. package/ietf/ietf.test.d.ts +0 -2
  270. package/ietf/ietf.test.d.ts.map +0 -1
  271. package/ietf/ietf.test.js +0 -873
  272. package/ietf/ietf.test.js.map +0 -1
  273. package/integration.test.d.ts +0 -2
  274. package/integration.test.d.ts.map +0 -1
  275. package/integration.test.js +0 -90
  276. package/integration.test.js.map +0 -1
  277. package/lite/origin.d.ts.map +0 -1
  278. package/lite/origin.js.map +0 -1
  279. package/lite/subscriber.test.d.ts +0 -2
  280. package/lite/subscriber.test.d.ts.map +0 -1
  281. package/lite/subscriber.test.js +0 -30
  282. package/lite/subscriber.test.js.map +0 -1
  283. package/lite/track.test.d.ts +0 -2
  284. package/lite/track.test.d.ts.map +0 -1
  285. package/lite/track.test.js +0 -53
  286. package/lite/track.test.js.map +0 -1
  287. package/path.test.d.ts +0 -2
  288. package/path.test.d.ts.map +0 -1
  289. package/path.test.js +0 -169
  290. package/path.test.js.map +0 -1
  291. package/stream.test.d.ts +0 -2
  292. package/stream.test.d.ts.map +0 -1
  293. package/stream.test.js +0 -210
  294. package/stream.test.js.map +0 -1
  295. package/track.test.d.ts +0 -2
  296. package/track.test.d.ts.map +0 -1
  297. package/track.test.js +0 -56
  298. package/track.test.js.map +0 -1
  299. package/util/error.d.ts +0 -3
  300. package/util/error.d.ts.map +0 -1
  301. package/util/error.js +0 -9
  302. package/util/error.js.map +0 -1
  303. package/util/index.d.ts +0 -2
  304. package/util/index.d.ts.map +0 -1
  305. package/util/index.js +0 -3
  306. package/util/index.js.map +0 -1
  307. package/varint.test.d.ts +0 -2
  308. package/varint.test.d.ts.map +0 -1
  309. package/varint.test.js +0 -106
  310. package/varint.test.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.d.ts","sourceRoot":"","sources":["../../src/ietf/cluster.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAY,KAAK,MAAM,EAAgC,MAAM,cAAc,CAAC;AACnF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAe,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,KAAK,WAAW,EAAW,MAAM,cAAc,CAAC;AAEzD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACpB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,MAAM;IACtB,qFAAqF;IACrF,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;yEACqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACb;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAYxF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,QAGjF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,IAAI,GAAG,SAAS,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAoBrD;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CASnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAuBrD"}
@@ -0,0 +1,204 @@
1
+ /* @ts-self-types="./cluster.d.ts" */
2
+ /**
3
+ * The MoQ Cluster extension (draft-lcurley-moq-cluster-00).
4
+ *
5
+ * moq-transport carries no routing information, so a peer cannot tell whether an
6
+ * advertisement already passed through us. This extension adds it:
7
+ *
8
+ * - each endpoint declares its own Hop ID via the RELAY_HOPS Setup Option, which is also
9
+ * what negotiates the extension;
10
+ * - every advertisement carries the HOP_PATH it traversed and the accumulated ROUTE_COST
11
+ * of that path, as Key-Value-Pair message parameters on PUBLISH_NAMESPACE and NAMESPACE.
12
+ *
13
+ * These are the semantics moq-lite carries natively on every announcement (see
14
+ * `lite/announce.ts`); this module is the moq-transport binding, negotiated on draft-17+
15
+ * only, where SETUP is a Key-Value-Pair block.
16
+ *
17
+ * We are a leaf, never a relay: the only path we can advertise is our own, so we stamp a
18
+ * single-entry HOP_PATH and seed the cost at 0. What we get out of declaring is the other
19
+ * direction. A relay that knows our Hop ID withholds the advertisements that already flowed
20
+ * through us, so publishing a broadcast no longer announces it back to us, which is what
21
+ * moq-lite has always done with its own hop chain.
22
+ *
23
+ * @module
24
+ * @internal
25
+ */
26
+ import { ProtocolViolation, reason } from "../error.js";
27
+ import { MAX_HOPS, OriginSchema, UNKNOWN_ORIGIN } from "../origin.js";
28
+ import * as Varint from "../varint.js";
29
+ import { Parameters, SetupOption } from "./parameters.js";
30
+ import { Version } from "./version.js";
31
+ /**
32
+ * Whether a version can negotiate the extension.
33
+ *
34
+ * Draft-17 is the first with the unified SETUP: a bare Key-Value-Pair block that a new
35
+ * option slots into without touching the message shape. Earlier drafts exchange SETUP over
36
+ * the legacy control stream, which this extension does not cover.
37
+ *
38
+ * @internal
39
+ */
40
+ export function supported(version) {
41
+ return version !== Version.DRAFT_14 && version !== Version.DRAFT_15 && version !== Version.DRAFT_16;
42
+ }
43
+ /**
44
+ * Read the peer's Hop ID out of its SETUP, or `undefined` when it did not negotiate.
45
+ *
46
+ * @internal
47
+ */
48
+ export function fromSetup(params, version) {
49
+ if (!supported(version))
50
+ return undefined;
51
+ // RELAY_HOPS is odd, so its value is a length-prefixed byte string holding the sender's
52
+ // Hop ID as a single varint. 0 is legal: the peer speaks the extension but withholds
53
+ // its identity.
54
+ const value = params.getBytes(SetupOption.RelayHops);
55
+ if (value === undefined)
56
+ return undefined;
57
+ const [origin, rest] = Varint.decodeLeadingOnes(value);
58
+ if (rest.length !== 0)
59
+ throw new Error("trailing bytes in RELAY_HOPS");
60
+ return OriginSchema.parse(origin);
61
+ }
62
+ /**
63
+ * Declare our own Hop ID, which is also what negotiates the extension.
64
+ *
65
+ * @internal
66
+ */
67
+ export function intoSetup(params, self, version) {
68
+ if (!supported(version))
69
+ return;
70
+ params.setBytes(SetupOption.RelayHops, Varint.encodeLeadingOnes(self));
71
+ }
72
+ /**
73
+ * Whether the session negotiated the extension, which is what decides whether an
74
+ * advertisement carries the parameters in either direction.
75
+ *
76
+ * @internal
77
+ */
78
+ export function negotiated(ids) {
79
+ return ids?.peer !== undefined;
80
+ }
81
+ /**
82
+ * The advertisement to send for one of our own broadcasts, or `undefined` on a session
83
+ * that negotiated nothing.
84
+ *
85
+ * The sender's own Hop ID is always the last entry, so a receiver reconstructs the full
86
+ * path without the sender restating it. We originate everything we advertise, so the path
87
+ * is just us and the cost is 0: a subscription costs the peer nothing but the transfer,
88
+ * since we are already producing the content.
89
+ *
90
+ * @internal
91
+ */
92
+ export function advertise(ids) {
93
+ if (ids === undefined || ids.peer === undefined)
94
+ return undefined;
95
+ return { hops: [ids.self], cost: 0n };
96
+ }
97
+ /**
98
+ * Whether this advertisement looped back through `self`.
99
+ *
100
+ * A receiver discards such an advertisement: forwarding it would extend the loop, and
101
+ * subscribing through it would route the receiver back to itself. Hop ID 0 identifies
102
+ * nothing, so a receiver that withheld its own identity detects nothing.
103
+ *
104
+ * @internal
105
+ */
106
+ export function loops(advert, self) {
107
+ return self !== UNKNOWN_ORIGIN && advert.hops.includes(self);
108
+ }
109
+ /**
110
+ * Write an advertisement's parameters, ready to encode onto the wire.
111
+ *
112
+ * @internal
113
+ */
114
+ export function intoParams(advert) {
115
+ validate(advert.hops);
116
+ const params = new Parameters();
117
+ // The entries fill the value, so they are written with no count of their own and framed
118
+ // by the parameter's own length prefix.
119
+ const hops = advert.hops.map((hop) => Varint.encodeLeadingOnes(hop));
120
+ const value = new Uint8Array(hops.reduce((total, hop) => total + hop.length, 0));
121
+ let offset = 0;
122
+ for (const hop of hops) {
123
+ value.set(hop, offset);
124
+ offset += hop.length;
125
+ }
126
+ params.hopPath = value;
127
+ // Absent means 0, so a free path sends nothing.
128
+ if (advert.cost !== 0n)
129
+ params.routeCost = advert.cost;
130
+ return params;
131
+ }
132
+ /**
133
+ * Read the parameters a negotiated session puts on every advertisement.
134
+ *
135
+ * The parameter block is mandatory there, so a message that ends before it (the base form)
136
+ * or one whose block does not parse is the peer's violation, same as a block that parses but
137
+ * carries no HOP_PATH.
138
+ *
139
+ * @internal
140
+ */
141
+ export async function decodeParams(r, version) {
142
+ let params;
143
+ try {
144
+ params = await Parameters.decode(r, version);
145
+ }
146
+ catch (err) {
147
+ throw new ProtocolViolation(reason(err), { cause: err });
148
+ }
149
+ return fromParams(params);
150
+ }
151
+ /**
152
+ * Read an advertisement out of a decoded parameter block.
153
+ *
154
+ * Every way this fails is one the draft says to close the session over: a negotiated session
155
+ * that omits HOP_PATH, entries that do not exactly fill the parameter, an empty list, or a
156
+ * non-zero Hop ID appearing twice. So they all surface as one {@link ProtocolViolation},
157
+ * which the session dispatch acts on.
158
+ *
159
+ * @internal
160
+ */
161
+ export function fromParams(params) {
162
+ try {
163
+ const value = params.hopPath;
164
+ if (value === undefined)
165
+ throw new Error("advertisement is missing HOP_PATH");
166
+ const hops = [];
167
+ let rest = value;
168
+ while (rest.length > 0) {
169
+ // A short read here means the entries did not exactly fill the length.
170
+ const [hop, remain] = Varint.decodeLeadingOnes(rest);
171
+ hops.push(OriginSchema.parse(hop));
172
+ rest = remain;
173
+ // Bail before the buffer does: a hostile length would otherwise cost us one
174
+ // allocation per entry all the way to 64KB of parameter value.
175
+ if (hops.length > MAX_HOPS)
176
+ throw new Error(`hop count exceeds maximum ${MAX_HOPS}`);
177
+ }
178
+ validate(hops);
179
+ return { hops, cost: params.routeCost ?? 0n };
180
+ }
181
+ catch (err) {
182
+ throw new ProtocolViolation(reason(err), { cause: err });
183
+ }
184
+ }
185
+ /**
186
+ * Reject a path that cannot have come from a conforming sender: an empty list, one longer
187
+ * than we accept, or a non-zero Hop ID appearing twice (a loop). Duplicate zeros are legal,
188
+ * since 0 identifies nothing.
189
+ */
190
+ function validate(hops) {
191
+ if (hops.length === 0)
192
+ throw new Error("hop path is empty");
193
+ if (hops.length > MAX_HOPS)
194
+ throw new Error(`hop count ${hops.length} exceeds maximum ${MAX_HOPS}`);
195
+ // MAX_HOPS is 32, so the quadratic scan is cheaper than allocating a set.
196
+ for (let i = 0; i < hops.length; i++) {
197
+ const hop = hops[i];
198
+ if (hop === UNKNOWN_ORIGIN)
199
+ continue;
200
+ if (hops.indexOf(hop, i + 1) !== -1)
201
+ throw new Error(`hop ${hop} appears twice in the hop path`);
202
+ }
203
+ }
204
+ //# sourceMappingURL=cluster.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster.js","sourceRoot":"","sources":["../../src/ietf/cluster.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The MoQ Cluster extension (draft-lcurley-moq-cluster-00).\n *\n * moq-transport carries no routing information, so a peer cannot tell whether an\n * advertisement already passed through us. This extension adds it:\n *\n * - each endpoint declares its own Hop ID via the RELAY_HOPS Setup Option, which is also\n * what negotiates the extension;\n * - every advertisement carries the HOP_PATH it traversed and the accumulated ROUTE_COST\n * of that path, as Key-Value-Pair message parameters on PUBLISH_NAMESPACE and NAMESPACE.\n *\n * These are the semantics moq-lite carries natively on every announcement (see\n * `lite/announce.ts`); this module is the moq-transport binding, negotiated on draft-17+\n * only, where SETUP is a Key-Value-Pair block.\n *\n * We are a leaf, never a relay: the only path we can advertise is our own, so we stamp a\n * single-entry HOP_PATH and seed the cost at 0. What we get out of declaring is the other\n * direction. A relay that knows our Hop ID withholds the advertisements that already flowed\n * through us, so publishing a broadcast no longer announces it back to us, which is what\n * moq-lite has always done with its own hop chain.\n *\n * @module\n * @internal\n */\n\nimport { ProtocolViolation, reason } from \"../error.ts\";\nimport { MAX_HOPS, type Origin, OriginSchema, UNKNOWN_ORIGIN } from \"../origin.ts\";\nimport type { Reader } from \"../stream.ts\";\nimport * as Varint from \"../varint.ts\";\nimport { Parameters, SetupOption, type SetupOptions } from \"./parameters.ts\";\nimport { type IetfVersion, Version } from \"./version.ts\";\n\n/**\n * Whether a version can negotiate the extension.\n *\n * Draft-17 is the first with the unified SETUP: a bare Key-Value-Pair block that a new\n * option slots into without touching the message shape. Earlier drafts exchange SETUP over\n * the legacy control stream, which this extension does not cover.\n *\n * @internal\n */\nexport function supported(version: IetfVersion): boolean {\n\treturn version !== Version.DRAFT_14 && version !== Version.DRAFT_15 && version !== Version.DRAFT_16;\n}\n\n/**\n * The Hop IDs this session declared, one per direction.\n *\n * @internal\n */\nexport interface Hops {\n\t/** Our own Hop ID, declared in SETUP and stamped onto every advertisement we send. */\n\tself: Origin;\n\n\t/**\n\t * The peer's Hop ID, or `undefined` when it declared no RELAY_HOPS.\n\t *\n\t * This is what decides whether advertisements carry the parameters at all, in both\n\t * directions: a peer that declared nothing has not read ours either, so sending it a\n\t * HOP_PATH would be a protocol violation and expecting one back would reject a legal\n\t * message. A peer that declared the reserved 0 negotiated the extension but withheld its\n\t * identity, so the parameters flow and there is simply nothing to exclude.\n\t */\n\tpeer?: Origin;\n}\n\n/**\n * The cluster parameters carried on one advertisement.\n *\n * Present on every PUBLISH_NAMESPACE and NAMESPACE of a session that negotiated the\n * extension, and on none of a session that did not.\n *\n * @internal\n */\nexport interface Advert {\n\t/** HOP_PATH: the path this advertisement traversed, publisher first, sender last. */\n\thops: Origin[];\n\n\t/** ROUTE_COST: the marginal cost of subscribing via this advertisement. Absent on the\n\t * wire means 0, so an endpoint that prices nothing sends nothing. */\n\tcost: bigint;\n}\n\n/**\n * Read the peer's Hop ID out of its SETUP, or `undefined` when it did not negotiate.\n *\n * @internal\n */\nexport function fromSetup(params: SetupOptions, version: IetfVersion): Origin | undefined {\n\tif (!supported(version)) return undefined;\n\n\t// RELAY_HOPS is odd, so its value is a length-prefixed byte string holding the sender's\n\t// Hop ID as a single varint. 0 is legal: the peer speaks the extension but withholds\n\t// its identity.\n\tconst value = params.getBytes(SetupOption.RelayHops);\n\tif (value === undefined) return undefined;\n\n\tconst [origin, rest] = Varint.decodeLeadingOnes(value);\n\tif (rest.length !== 0) throw new Error(\"trailing bytes in RELAY_HOPS\");\n\treturn OriginSchema.parse(origin);\n}\n\n/**\n * Declare our own Hop ID, which is also what negotiates the extension.\n *\n * @internal\n */\nexport function intoSetup(params: SetupOptions, self: Origin, version: IetfVersion) {\n\tif (!supported(version)) return;\n\tparams.setBytes(SetupOption.RelayHops, Varint.encodeLeadingOnes(self));\n}\n\n/**\n * Whether the session negotiated the extension, which is what decides whether an\n * advertisement carries the parameters in either direction.\n *\n * @internal\n */\nexport function negotiated(ids: Hops | undefined): boolean {\n\treturn ids?.peer !== undefined;\n}\n\n/**\n * The advertisement to send for one of our own broadcasts, or `undefined` on a session\n * that negotiated nothing.\n *\n * The sender's own Hop ID is always the last entry, so a receiver reconstructs the full\n * path without the sender restating it. We originate everything we advertise, so the path\n * is just us and the cost is 0: a subscription costs the peer nothing but the transfer,\n * since we are already producing the content.\n *\n * @internal\n */\nexport function advertise(ids: Hops | undefined): Advert | undefined {\n\tif (ids === undefined || ids.peer === undefined) return undefined;\n\treturn { hops: [ids.self], cost: 0n };\n}\n\n/**\n * Whether this advertisement looped back through `self`.\n *\n * A receiver discards such an advertisement: forwarding it would extend the loop, and\n * subscribing through it would route the receiver back to itself. Hop ID 0 identifies\n * nothing, so a receiver that withheld its own identity detects nothing.\n *\n * @internal\n */\nexport function loops(advert: Advert, self: Origin): boolean {\n\treturn self !== UNKNOWN_ORIGIN && advert.hops.includes(self);\n}\n\n/**\n * Write an advertisement's parameters, ready to encode onto the wire.\n *\n * @internal\n */\nexport function intoParams(advert: Advert): Parameters {\n\tvalidate(advert.hops);\n\n\tconst params = new Parameters();\n\n\t// The entries fill the value, so they are written with no count of their own and framed\n\t// by the parameter's own length prefix.\n\tconst hops = advert.hops.map((hop) => Varint.encodeLeadingOnes(hop));\n\tconst value = new Uint8Array(hops.reduce((total, hop) => total + hop.length, 0));\n\tlet offset = 0;\n\tfor (const hop of hops) {\n\t\tvalue.set(hop, offset);\n\t\toffset += hop.length;\n\t}\n\tparams.hopPath = value;\n\n\t// Absent means 0, so a free path sends nothing.\n\tif (advert.cost !== 0n) params.routeCost = advert.cost;\n\n\treturn params;\n}\n\n/**\n * Read the parameters a negotiated session puts on every advertisement.\n *\n * The parameter block is mandatory there, so a message that ends before it (the base form)\n * or one whose block does not parse is the peer's violation, same as a block that parses but\n * carries no HOP_PATH.\n *\n * @internal\n */\nexport async function decodeParams(r: Reader, version: IetfVersion): Promise<Advert> {\n\tlet params: Parameters;\n\ttry {\n\t\tparams = await Parameters.decode(r, version);\n\t} catch (err) {\n\t\tthrow new ProtocolViolation(reason(err), { cause: err });\n\t}\n\n\treturn fromParams(params);\n}\n\n/**\n * Read an advertisement out of a decoded parameter block.\n *\n * Every way this fails is one the draft says to close the session over: a negotiated session\n * that omits HOP_PATH, entries that do not exactly fill the parameter, an empty list, or a\n * non-zero Hop ID appearing twice. So they all surface as one {@link ProtocolViolation},\n * which the session dispatch acts on.\n *\n * @internal\n */\nexport function fromParams(params: Parameters): Advert {\n\ttry {\n\t\tconst value = params.hopPath;\n\t\tif (value === undefined) throw new Error(\"advertisement is missing HOP_PATH\");\n\n\t\tconst hops: Origin[] = [];\n\t\tlet rest = value;\n\t\twhile (rest.length > 0) {\n\t\t\t// A short read here means the entries did not exactly fill the length.\n\t\t\tconst [hop, remain] = Varint.decodeLeadingOnes(rest);\n\t\t\thops.push(OriginSchema.parse(hop));\n\t\t\trest = remain;\n\n\t\t\t// Bail before the buffer does: a hostile length would otherwise cost us one\n\t\t\t// allocation per entry all the way to 64KB of parameter value.\n\t\t\tif (hops.length > MAX_HOPS) throw new Error(`hop count exceeds maximum ${MAX_HOPS}`);\n\t\t}\n\n\t\tvalidate(hops);\n\t\treturn { hops, cost: params.routeCost ?? 0n };\n\t} catch (err) {\n\t\tthrow new ProtocolViolation(reason(err), { cause: err });\n\t}\n}\n\n/**\n * Reject a path that cannot have come from a conforming sender: an empty list, one longer\n * than we accept, or a non-zero Hop ID appearing twice (a loop). Duplicate zeros are legal,\n * since 0 identifies nothing.\n */\nfunction validate(hops: Origin[]) {\n\tif (hops.length === 0) throw new Error(\"hop path is empty\");\n\tif (hops.length > MAX_HOPS) throw new Error(`hop count ${hops.length} exceeds maximum ${MAX_HOPS}`);\n\n\t// MAX_HOPS is 32, so the quadratic scan is cheaper than allocating a set.\n\tfor (let i = 0; i < hops.length; i++) {\n\t\tconst hop = hops[i];\n\t\tif (hop === UNKNOWN_ORIGIN) continue;\n\t\tif (hops.indexOf(hop, i + 1) !== -1) throw new Error(`hop ${hop} appears twice in the hop path`);\n\t}\n}\n"]}
@@ -1,8 +1,12 @@
1
- import type { Announced } from "../announced";
2
- import type { Broadcast } from "../broadcast";
1
+ import { type Getter } from "@norskvideo/moq-signals";
2
+ import * as announce from "../announced";
3
+ import type * as broadcast from "../broadcast";
3
4
  import type { Established } from "../connection/established";
5
+ import { type Probe, type Stats } from "../connection/stats";
6
+ import { type Transport } from "../connection/transport";
4
7
  import * as Path from "../path";
5
8
  import { type Stream } from "../stream";
9
+ import * as Cluster from "./cluster";
6
10
  import { type IetfVersion } from "./version";
7
11
  /**
8
12
  * Represents a connection to a MoQ server using moq-transport protocol.
@@ -13,6 +17,11 @@ export declare class Connection implements Established {
13
17
  #private;
14
18
  readonly url: URL;
15
19
  readonly version: string;
20
+ readonly transport: Transport;
21
+ /** Whether the relay supports broadcast discovery; see {@link Established.discovery}. */
22
+ readonly discovery: boolean;
23
+ /** moq-transport has no PROBE, so this stays empty; see {@link Established.probe}. */
24
+ readonly probe: Getter<Probe>;
16
25
  /**
17
26
  * Creates a new Connection instance.
18
27
  * @param url - The URL of the connection
@@ -20,16 +29,33 @@ export declare class Connection implements Established {
20
29
  * @param control - The control/setup stream
21
30
  * @param maxRequestId - The initial max request ID
22
31
  * @param version - The negotiated protocol version
32
+ * @param solicit - What the peer's SETUP declared (undefined when it declared nothing)
33
+ * @param cluster - The Hop IDs the SETUP exchange settled, on the versions that negotiate them
23
34
  *
24
35
  * @internal
25
36
  */
26
- constructor({ url, quic, control, maxRequestId, version, }: {
37
+ constructor({ url, quic, control, maxRequestId, version, client, discovery, solicit, cluster, }: {
27
38
  url: URL;
28
39
  quic: WebTransport;
29
40
  control: Stream;
30
41
  maxRequestId: bigint;
31
42
  version: IetfVersion;
43
+ /** Whether this peer initiated the session, selecting the even request-ID space. */
44
+ client: boolean;
45
+ discovery?: boolean;
46
+ /**
47
+ * What the peer declared about being solicited. `undefined` means it declared
48
+ * nothing, which is the one case where announcing at us unasked is not a bug.
49
+ */
50
+ solicit?: boolean;
51
+ /**
52
+ * The Hop IDs this session declared (MoQ Cluster). `undefined` on a version that
53
+ * cannot negotiate the extension, as is a `peer` the peer never declared.
54
+ */
55
+ cluster?: Cluster.Hops;
32
56
  });
57
+ /** Snapshot the transport's counters; see {@link Established.stats}. */
58
+ stats(): Promise<Stats>;
33
59
  /**
34
60
  * Closes the connection.
35
61
  */
@@ -39,13 +65,13 @@ export declare class Connection implements Established {
39
65
  * @param name - The broadcast path to publish
40
66
  * @param broadcast - The broadcast to publish
41
67
  */
42
- publish(path: Path.Valid, broadcast: Broadcast): void;
68
+ publish(path: Path.Valid, producer: broadcast.Producer): void;
43
69
  /**
44
70
  * Gets an announced reader for the specified prefix.
45
71
  * @param prefix - The prefix for announcements
46
72
  * @returns An Announced instance
47
73
  */
48
- announced(prefix?: Path.Valid): Announced;
74
+ announced(prefix?: Path.Valid): announce.Consumer;
49
75
  /**
50
76
  * Consumes a broadcast from the connection.
51
77
  *
@@ -55,7 +81,14 @@ export declare class Connection implements Established {
55
81
  * @param broadcast - The path of the broadcast to consume
56
82
  * @returns A Broadcast instance
57
83
  */
58
- consume(broadcast: Path.Valid): Broadcast;
84
+ consume(path: Path.Valid): broadcast.Consumer;
85
+ /**
86
+ * Watches a broadcast, live only while it is announced.
87
+ *
88
+ * @param path - The path of the broadcast to watch
89
+ * @returns A reactive handle to the broadcast
90
+ */
91
+ announcedBroadcast(path: Path.Valid): announce.Broadcast;
59
92
  /**
60
93
  * Returns a promise that resolves when the connection is closed.
61
94
  * @returns A promise that resolves when closed
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/ietf/connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,IAAI,MAAM,YAAY,CAAC;AACnC,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAWjE,OAAO,EAAE,KAAK,WAAW,EAAwB,MAAM,cAAc,CAAC;AAEtE;;;;GAIG;AACH,qBAAa,UAAW,YAAW,WAAW;;IAE7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAGlB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAiBzB;;;;;;;;;OASG;IACH,YAAY,EACX,GAAG,EACH,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,OAAO,GACP,EAAE;QACF,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,WAAW,CAAC;KACrB,EAwBA;IAED;;OAEG;IACH,KAAK,SAYJ;IAcD;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,QAE7C;IAED;;;;OAIG;IACH,SAAS,CAAC,MAAM,aAAe,GAAG,SAAS,CAE1C;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAExC;IA2HD;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACD"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/ietf/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,KAAK,SAAS,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAkB,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,4BAA4B,CAAC;AAEzE,OAAO,KAAK,IAAI,MAAM,YAAY,CAAC;AACnC,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAUxC,OAAO,EAAE,KAAK,WAAW,EAAwB,MAAM,cAAc,CAAC;AAEtE;;;;GAIG;AACH,qBAAa,UAAW,YAAW,WAAW;;IAE7C,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAGlB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAGzB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAE9B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAyB;IAuBtD;;;;;;;;;;;OAWG;IACH,YAAY,EACX,GAAG,EACH,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,OAAO,EACP,MAAM,EACN,SAAgB,EAChB,OAAO,EACP,OAAO,GACP,EAAE;QACF,GAAG,EAAE,GAAG,CAAC;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,WAAW,CAAC;QACrB,oFAAoF;QACpF,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC;KACvB,EAiCA;IAED,wEAAwE;IAClE,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAE5B;IAED;;OAEG;IACH,KAAK,SAaJ;IAcD;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,QAErD;IAED;;;;OAIG;IACH,SAAS,CAAC,MAAM,aAAe,GAAG,QAAQ,CAAC,QAAQ,CAElD;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC,QAAQ,CAE5C;IAED;;;;;OAKG;IACH,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,SAAS,CAEvD;IAuJD;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;CACD"}
@@ -1,7 +1,13 @@
1
1
  /* @ts-self-types="./connection.d.ts" */
2
+ import { Signal } from "@norskvideo/moq-signals";
3
+ import * as announce from "../announced.js";
4
+ import { transportStats } from "../connection/stats.js";
5
+ import { transportOf } from "../connection/transport.js";
6
+ import { ProtocolViolation } from "../error.js";
2
7
  import * as Path from "../path.js";
3
8
  import { Readers } from "../stream.js";
4
9
  import { ControlStreamAdapter, NativeSession } from "./adapter.js";
10
+ import * as Cluster from "./cluster.js";
5
11
  import { GoAway } from "./goaway.js";
6
12
  import { Group } from "./object.js";
7
13
  import { Publish } from "./publish.js";
@@ -22,6 +28,12 @@ export class Connection {
22
28
  url;
23
29
  // The negotiated protocol version.
24
30
  version;
31
+ // The wire transport this session runs over.
32
+ transport;
33
+ /** Whether the relay supports broadcast discovery; see {@link Established.discovery}. */
34
+ discovery;
35
+ /** moq-transport has no PROBE, so this stays empty; see {@link Established.probe}. */
36
+ probe = new Signal({});
25
37
  // The established WebTransport session.
26
38
  #quic;
27
39
  // Session abstraction: adapter for v14-v16, native for v17.
@@ -30,6 +42,10 @@ export class Connection {
30
42
  #publisher;
31
43
  // Module for distributing tracks.
32
44
  #subscriber;
45
+ // What the peer declared about being solicited; see {@link Ietf.solicitFromSetup}.
46
+ #solicit;
47
+ // The Hop IDs this session declared; see {@link Cluster}.
48
+ #cluster;
33
49
  // Just to avoid logging when `close()` is called.
34
50
  #closed = false;
35
51
  /**
@@ -39,21 +55,25 @@ export class Connection {
39
55
  * @param control - The control/setup stream
40
56
  * @param maxRequestId - The initial max request ID
41
57
  * @param version - The negotiated protocol version
58
+ * @param solicit - What the peer's SETUP declared (undefined when it declared nothing)
59
+ * @param cluster - The Hop IDs the SETUP exchange settled, on the versions that negotiate them
42
60
  *
43
61
  * @internal
44
62
  */
45
- constructor({ url, quic, control, maxRequestId, version, }) {
63
+ constructor({ url, quic, control, maxRequestId, version, client, discovery = true, solicit, cluster, }) {
46
64
  this.url = url;
65
+ this.discovery = discovery;
47
66
  this.version = versionName(version);
67
+ this.transport = transportOf(quic);
48
68
  this.#quic = quic;
49
69
  // Two-path dispatch: v14-v16 uses adapter, v17+ uses native bidi streams
50
70
  if (version >= Version.DRAFT_17) {
51
- this.#session = new NativeSession(quic, version);
71
+ this.#session = new NativeSession(quic, version, client);
52
72
  // v17+: control/setup stream only carries GoAway
53
73
  void this.#runGoAway(control, version);
54
74
  }
55
75
  else {
56
- const adapter = new ControlStreamAdapter(quic, control, version, maxRequestId);
76
+ const adapter = new ControlStreamAdapter(quic, control, version, maxRequestId, client);
57
77
  this.#session = adapter;
58
78
  // Start the adapter read loop (routes control messages to virtual streams)
59
79
  void adapter.run().catch((err) => {
@@ -62,10 +82,21 @@ export class Connection {
62
82
  this.close();
63
83
  });
64
84
  }
65
- this.#publisher = new Publisher(this.#quic, this.#session);
66
- this.#subscriber = new Subscriber(this.#session);
85
+ this.#publisher = new Publisher({
86
+ quic: this.#quic,
87
+ session: this.#session,
88
+ requiresSolicitation: solicit ?? false,
89
+ cluster,
90
+ });
91
+ this.#solicit = solicit;
92
+ this.#cluster = cluster;
93
+ this.#subscriber = new Subscriber({ session: this.#session, cluster });
67
94
  void this.#run();
68
95
  }
96
+ /** Snapshot the transport's counters; see {@link Established.stats}. */
97
+ async stats() {
98
+ return transportStats(this.#quic);
99
+ }
69
100
  /**
70
101
  * Closes the connection.
71
102
  */
@@ -73,6 +104,7 @@ export class Connection {
73
104
  if (this.#closed)
74
105
  return;
75
106
  this.#closed = true;
107
+ this.#publisher.close();
76
108
  this.#session.close();
77
109
  try {
78
110
  this.#quic.close();
@@ -83,7 +115,7 @@ export class Connection {
83
115
  }
84
116
  async #run() {
85
117
  try {
86
- await Promise.all([this.#runBidis(), this.#runUnis()]);
118
+ await Promise.all([this.#runBidis(), this.#runUnis(), this.#publisher.runPublishNamespaces()]);
87
119
  }
88
120
  catch (err) {
89
121
  if (!this.#closed) {
@@ -99,8 +131,8 @@ export class Connection {
99
131
  * @param name - The broadcast path to publish
100
132
  * @param broadcast - The broadcast to publish
101
133
  */
102
- publish(path, broadcast) {
103
- this.#publisher.publish(path, broadcast);
134
+ publish(path, producer) {
135
+ this.#publisher.publish(path, producer);
104
136
  }
105
137
  /**
106
138
  * Gets an announced reader for the specified prefix.
@@ -119,8 +151,17 @@ export class Connection {
119
151
  * @param broadcast - The path of the broadcast to consume
120
152
  * @returns A Broadcast instance
121
153
  */
122
- consume(broadcast) {
123
- return this.#subscriber.consume(broadcast);
154
+ consume(path) {
155
+ return this.#subscriber.consume(path);
156
+ }
157
+ /**
158
+ * Watches a broadcast, live only while it is announced.
159
+ *
160
+ * @param path - The path of the broadcast to watch
161
+ * @returns A reactive handle to the broadcast
162
+ */
163
+ announcedBroadcast(path) {
164
+ return new announce.Broadcast({ connection: this, path });
124
165
  }
125
166
  /**
126
167
  * Accepts bidi streams (virtual for v14-v16, real for v17) and dispatches.
@@ -133,11 +174,15 @@ export class Connection {
133
174
  void this.#runBidi(stream).catch((err) => {
134
175
  console.error("error processing bidi stream", err);
135
176
  stream.abort(new Error("bidi stream error"));
177
+ // The peer broke the protocol, so losing the stream is not enough: nothing
178
+ // stops it repeating the violation on the next one.
179
+ if (err instanceof ProtocolViolation)
180
+ this.close();
136
181
  });
137
182
  }
138
183
  }
139
184
  /**
140
- * Unified bidi stream dispatch reads typeId and routes to handler.
185
+ * Unified bidi stream dispatch. Reads typeId and routes to handler.
141
186
  * Matches the lite module's runBidi pattern.
142
187
  */
143
188
  async #runBidi(stream) {
@@ -174,7 +219,23 @@ export class Connection {
174
219
  }
175
220
  // Subscriber handles incoming notifications
176
221
  case PublishNamespace.id: {
177
- const msg = await PublishNamespace.decode(stream.reader, this.#session.version);
222
+ const msg = await PublishNamespace.decode(stream.reader, this.#session.version, Cluster.negotiated(this.#cluster));
223
+ // We always declare that advertisements to us must be solicited (MoQ
224
+ // Solicit), and writing the option at all proves the peer implements the
225
+ // extension, whichever value it chose. It also cannot have advertised
226
+ // before reading our SETUP, since our SETUP is what says whether
227
+ // advertising unasked is allowed. So this is a bug in the peer, and a
228
+ // silent one on both sides if we tolerate it.
229
+ //
230
+ // Draft-14/15 are exempt: they have no inline NAMESPACE, so a
231
+ // PUBLISH_NAMESPACE request is also how a peer answers our
232
+ // SUBSCRIBE_NAMESPACE there, and the message alone does not say which.
233
+ const legacy = this.#session.version === Version.DRAFT_14 || this.#session.version === Version.DRAFT_15;
234
+ if (this.#solicit !== undefined && !legacy) {
235
+ console.error(`unsolicited publish_namespace from a peer that implements MoQ Solicit: broadcast=${msg.trackNamespace}`);
236
+ this.close();
237
+ break;
238
+ }
178
239
  await this.#subscriber.runPublishNamespace(msg, stream);
179
240
  break;
180
241
  }
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/ietf/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Announced } from \"../announced.ts\";\nimport type { Broadcast } from \"../broadcast.ts\";\nimport type { Established } from \"../connection/established.ts\";\nimport * as Path from \"../path.ts\";\nimport { type Reader, Readers, type Stream } from \"../stream.ts\";\nimport { ControlStreamAdapter, NativeSession, type Session } from \"./adapter.ts\";\nimport { GoAway } from \"./goaway.ts\";\nimport { Group } from \"./object.ts\";\nimport { Publish } from \"./publish.ts\";\nimport { PublishNamespace } from \"./publish_namespace.ts\";\nimport { Publisher } from \"./publisher.ts\";\nimport { Subscribe, SubscribeUpdate } from \"./subscribe.ts\";\nimport { SubscribeNamespace, SubscribeNamespaceLegacy } from \"./subscribe_namespace.ts\";\nimport { Subscriber } from \"./subscriber.ts\";\nimport { TrackStatusRequest } from \"./track.ts\";\nimport { type IetfVersion, Version, versionName } from \"./version.ts\";\n\n/**\n * Represents a connection to a MoQ server using moq-transport protocol.\n *\n * @public\n */\nexport class Connection implements Established {\n\t// The URL of the connection.\n\treadonly url: URL;\n\n\t// The negotiated protocol version.\n\treadonly version: string;\n\n\t// The established WebTransport session.\n\t#quic: WebTransport;\n\n\t// Session abstraction: adapter for v14-v16, native for v17.\n\t#session: Session;\n\n\t// Module for contributing tracks.\n\t#publisher: Publisher;\n\n\t// Module for distributing tracks.\n\t#subscriber: Subscriber;\n\n\t// Just to avoid logging when `close()` is called.\n\t#closed = false;\n\n\t/**\n\t * Creates a new Connection instance.\n\t * @param url - The URL of the connection\n\t * @param quic - The WebTransport session\n\t * @param control - The control/setup stream\n\t * @param maxRequestId - The initial max request ID\n\t * @param version - The negotiated protocol version\n\t *\n\t * @internal\n\t */\n\tconstructor({\n\t\turl,\n\t\tquic,\n\t\tcontrol,\n\t\tmaxRequestId,\n\t\tversion,\n\t}: {\n\t\turl: URL;\n\t\tquic: WebTransport;\n\t\tcontrol: Stream;\n\t\tmaxRequestId: bigint;\n\t\tversion: IetfVersion;\n\t}) {\n\t\tthis.url = url;\n\t\tthis.version = versionName(version);\n\t\tthis.#quic = quic;\n\n\t\t// Two-path dispatch: v14-v16 uses adapter, v17+ uses native bidi streams\n\t\tif (version >= Version.DRAFT_17) {\n\t\t\tthis.#session = new NativeSession(quic, version);\n\t\t\t// v17+: control/setup stream only carries GoAway\n\t\t\tvoid this.#runGoAway(control, version);\n\t\t} else {\n\t\t\tconst adapter = new ControlStreamAdapter(quic, control, version, maxRequestId);\n\t\t\tthis.#session = adapter;\n\t\t\t// Start the adapter read loop (routes control messages to virtual streams)\n\t\t\tvoid adapter.run().catch((err: unknown) => {\n\t\t\t\tif (!this.#closed) console.error(\"adapter error\", err);\n\t\t\t\tthis.close();\n\t\t\t});\n\t\t}\n\n\t\tthis.#publisher = new Publisher(this.#quic, this.#session);\n\t\tthis.#subscriber = new Subscriber(this.#session);\n\n\t\tvoid this.#run();\n\t}\n\n\t/**\n\t * Closes the connection.\n\t */\n\tclose() {\n\t\tif (this.#closed) return;\n\n\t\tthis.#closed = true;\n\n\t\tthis.#session.close();\n\n\t\ttry {\n\t\t\tthis.#quic.close();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync #run(): Promise<void> {\n\t\ttry {\n\t\t\tawait Promise.all([this.#runBidis(), this.#runUnis()]);\n\t\t} catch (err) {\n\t\t\tif (!this.#closed) {\n\t\t\t\tconsole.error(\"fatal error running connection\", err);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\t/**\n\t * Publishes a broadcast to the connection.\n\t * @param name - The broadcast path to publish\n\t * @param broadcast - The broadcast to publish\n\t */\n\tpublish(path: Path.Valid, broadcast: Broadcast) {\n\t\tthis.#publisher.publish(path, broadcast);\n\t}\n\n\t/**\n\t * Gets an announced reader for the specified prefix.\n\t * @param prefix - The prefix for announcements\n\t * @returns An Announced instance\n\t */\n\tannounced(prefix = Path.empty()): Announced {\n\t\treturn this.#subscriber.announced(prefix);\n\t}\n\n\t/**\n\t * Consumes a broadcast from the connection.\n\t *\n\t * @remarks\n\t * If the broadcast is not found, a \"not found\" error will be thrown when requesting any tracks.\n\t *\n\t * @param broadcast - The path of the broadcast to consume\n\t * @returns A Broadcast instance\n\t */\n\tconsume(broadcast: Path.Valid): Broadcast {\n\t\treturn this.#subscriber.consume(broadcast);\n\t}\n\n\t/**\n\t * Accepts bidi streams (virtual for v14-v16, real for v17) and dispatches.\n\t */\n\tasync #runBidis() {\n\t\tfor (;;) {\n\t\t\tconst stream = await this.#session.acceptBi();\n\t\t\tif (!stream) break;\n\n\t\t\tvoid this.#runBidi(stream).catch((err: unknown) => {\n\t\t\t\tconsole.error(\"error processing bidi stream\", err);\n\t\t\t\tstream.abort(new Error(\"bidi stream error\"));\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Unified bidi stream dispatch — reads typeId and routes to handler.\n\t * Matches the lite module's runBidi pattern.\n\t */\n\tasync #runBidi(stream: Stream) {\n\t\tconst typeId = await stream.reader.u53();\n\n\t\tswitch (typeId) {\n\t\t\t// Draft-18 SUBSCRIBE_NAMESPACE (0x50) and the legacy 0x11 message decode\n\t\t\t// to the same request_id + namespace; the legacy options field is ignored.\n\t\t\tcase SubscribeNamespace.id: {\n\t\t\t\tconst msg = await SubscribeNamespace.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runSubscribeNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SubscribeNamespaceLegacy.id: {\n\t\t\t\tconst legacy = await SubscribeNamespaceLegacy.decode(stream.reader, this.#session.version);\n\t\t\t\tconst msg = new SubscribeNamespace({ requestId: legacy.requestId, namespace: legacy.namespace });\n\t\t\t\tawait this.#publisher.runSubscribeNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SubscribeUpdate.id: {\n\t\t\t\t// REQUEST_UPDATE (0x02) is a follow-up, not a valid initial message\n\t\t\t\tstream.abort(new Error(\"unexpected REQUEST_UPDATE as initial message\"));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Publisher handles incoming requests\n\t\t\tcase Subscribe.id: {\n\t\t\t\tconst msg = await Subscribe.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runSubscribe(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase TrackStatusRequest.id: {\n\t\t\t\tconst msg = await TrackStatusRequest.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runTrackStatusRequest(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Subscriber handles incoming notifications\n\t\t\tcase PublishNamespace.id: {\n\t\t\t\tconst msg = await PublishNamespace.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#subscriber.runPublishNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Publish.id: {\n\t\t\t\tconst msg = await Publish.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#subscriber.runPublish(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tconsole.warn(`unexpected bidi stream type: 0x${typeId.toString(16)}`);\n\t\t\t\tstream.abort(new Error(\"unexpected stream type\"));\n\t\t}\n\t}\n\n\t/**\n\t * Handles unidirectional streams for media delivery (groups).\n\t */\n\tasync #runUnis() {\n\t\tconst readers = new Readers(this.#quic, this.#session.version);\n\n\t\tfor (;;) {\n\t\t\tconst stream = await readers.next();\n\t\t\tif (!stream) break;\n\n\t\t\tthis.#runUni(stream)\n\t\t\t\t.then(() => {\n\t\t\t\t\tstream.stop(new Error(\"cancel\"));\n\t\t\t\t})\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconsole.error(\"error processing object stream\", err);\n\t\t\t\t\tstream.stop(err);\n\t\t\t\t});\n\t\t}\n\t}\n\n\tasync #runUni(stream: Reader) {\n\t\tconst header = await Group.decode(stream, this.#session.version);\n\t\tawait this.#subscriber.handleGroup(header, stream);\n\t}\n\n\t/**\n\t * v17+ only: reads GoAway from the setup/control stream.\n\t */\n\tasync #runGoAway(controlStream: Stream, version: IetfVersion) {\n\t\ttry {\n\t\t\tconst done = await controlStream.reader.done();\n\t\t\tif (done) return;\n\n\t\t\tconst typeId = await controlStream.reader.u53();\n\t\t\tif (typeId === GoAway.id) {\n\t\t\t\tconst msg = await GoAway.decode(controlStream.reader, version);\n\t\t\t\tconsole.warn(`received GOAWAY with redirect URI: ${msg.newSessionUri}`);\n\t\t\t} else {\n\t\t\t\tconsole.warn(`unexpected message on setup stream: 0x${typeId.toString(16)}`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (!this.#closed) {\n\t\t\t\tconsole.error(\"error reading setup stream\", err);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the connection is closed.\n\t * @returns A promise that resolves when closed\n\t */\n\tget closed(): Promise<void> {\n\t\treturn this.#quic.closed.then(() => undefined);\n\t}\n}\n"]}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/ietf/connection.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Getter, Signal } from \"@moq/signals\";\nimport * as announce from \"../announced.ts\";\nimport type * as broadcast from \"../broadcast.ts\";\nimport type { Established } from \"../connection/established.ts\";\nimport { type Probe, type Stats, transportStats } from \"../connection/stats.ts\";\nimport { type Transport, transportOf } from \"../connection/transport.ts\";\nimport { ProtocolViolation } from \"../error.ts\";\nimport * as Path from \"../path.ts\";\nimport { type Reader, Readers, type Stream } from \"../stream.ts\";\nimport { ControlStreamAdapter, NativeSession, type Session } from \"./adapter.ts\";\nimport * as Cluster from \"./cluster.ts\";\nimport { GoAway } from \"./goaway.ts\";\nimport { Group } from \"./object.ts\";\nimport { Publish } from \"./publish.ts\";\nimport { PublishNamespace } from \"./publish_namespace.ts\";\nimport { Publisher } from \"./publisher.ts\";\nimport { Subscribe, SubscribeUpdate } from \"./subscribe.ts\";\nimport { SubscribeNamespace, SubscribeNamespaceLegacy } from \"./subscribe_namespace.ts\";\nimport { Subscriber } from \"./subscriber.ts\";\nimport { TrackStatusRequest } from \"./track.ts\";\nimport { type IetfVersion, Version, versionName } from \"./version.ts\";\n\n/**\n * Represents a connection to a MoQ server using moq-transport protocol.\n *\n * @public\n */\nexport class Connection implements Established {\n\t// The URL of the connection.\n\treadonly url: URL;\n\n\t// The negotiated protocol version.\n\treadonly version: string;\n\n\t// The wire transport this session runs over.\n\treadonly transport: Transport;\n\n\t/** Whether the relay supports broadcast discovery; see {@link Established.discovery}. */\n\treadonly discovery: boolean;\n\n\t/** moq-transport has no PROBE, so this stays empty; see {@link Established.probe}. */\n\treadonly probe: Getter<Probe> = new Signal<Probe>({});\n\n\t// The established WebTransport session.\n\t#quic: WebTransport;\n\n\t// Session abstraction: adapter for v14-v16, native for v17.\n\t#session: Session;\n\n\t// Module for contributing tracks.\n\t#publisher: Publisher;\n\n\t// Module for distributing tracks.\n\t#subscriber: Subscriber;\n\n\t// What the peer declared about being solicited; see {@link Ietf.solicitFromSetup}.\n\t#solicit: boolean | undefined;\n\n\t// The Hop IDs this session declared; see {@link Cluster}.\n\t#cluster?: Cluster.Hops;\n\n\t// Just to avoid logging when `close()` is called.\n\t#closed = false;\n\n\t/**\n\t * Creates a new Connection instance.\n\t * @param url - The URL of the connection\n\t * @param quic - The WebTransport session\n\t * @param control - The control/setup stream\n\t * @param maxRequestId - The initial max request ID\n\t * @param version - The negotiated protocol version\n\t * @param solicit - What the peer's SETUP declared (undefined when it declared nothing)\n\t * @param cluster - The Hop IDs the SETUP exchange settled, on the versions that negotiate them\n\t *\n\t * @internal\n\t */\n\tconstructor({\n\t\turl,\n\t\tquic,\n\t\tcontrol,\n\t\tmaxRequestId,\n\t\tversion,\n\t\tclient,\n\t\tdiscovery = true,\n\t\tsolicit,\n\t\tcluster,\n\t}: {\n\t\turl: URL;\n\t\tquic: WebTransport;\n\t\tcontrol: Stream;\n\t\tmaxRequestId: bigint;\n\t\tversion: IetfVersion;\n\t\t/** Whether this peer initiated the session, selecting the even request-ID space. */\n\t\tclient: boolean;\n\t\tdiscovery?: boolean;\n\t\t/**\n\t\t * What the peer declared about being solicited. `undefined` means it declared\n\t\t * nothing, which is the one case where announcing at us unasked is not a bug.\n\t\t */\n\t\tsolicit?: boolean;\n\t\t/**\n\t\t * The Hop IDs this session declared (MoQ Cluster). `undefined` on a version that\n\t\t * cannot negotiate the extension, as is a `peer` the peer never declared.\n\t\t */\n\t\tcluster?: Cluster.Hops;\n\t}) {\n\t\tthis.url = url;\n\t\tthis.discovery = discovery;\n\t\tthis.version = versionName(version);\n\t\tthis.transport = transportOf(quic);\n\t\tthis.#quic = quic;\n\n\t\t// Two-path dispatch: v14-v16 uses adapter, v17+ uses native bidi streams\n\t\tif (version >= Version.DRAFT_17) {\n\t\t\tthis.#session = new NativeSession(quic, version, client);\n\t\t\t// v17+: control/setup stream only carries GoAway\n\t\t\tvoid this.#runGoAway(control, version);\n\t\t} else {\n\t\t\tconst adapter = new ControlStreamAdapter(quic, control, version, maxRequestId, client);\n\t\t\tthis.#session = adapter;\n\t\t\t// Start the adapter read loop (routes control messages to virtual streams)\n\t\t\tvoid adapter.run().catch((err: unknown) => {\n\t\t\t\tif (!this.#closed) console.error(\"adapter error\", err);\n\t\t\t\tthis.close();\n\t\t\t});\n\t\t}\n\n\t\tthis.#publisher = new Publisher({\n\t\t\tquic: this.#quic,\n\t\t\tsession: this.#session,\n\t\t\trequiresSolicitation: solicit ?? false,\n\t\t\tcluster,\n\t\t});\n\t\tthis.#solicit = solicit;\n\t\tthis.#cluster = cluster;\n\t\tthis.#subscriber = new Subscriber({ session: this.#session, cluster });\n\n\t\tvoid this.#run();\n\t}\n\n\t/** Snapshot the transport's counters; see {@link Established.stats}. */\n\tasync stats(): Promise<Stats> {\n\t\treturn transportStats(this.#quic);\n\t}\n\n\t/**\n\t * Closes the connection.\n\t */\n\tclose() {\n\t\tif (this.#closed) return;\n\n\t\tthis.#closed = true;\n\n\t\tthis.#publisher.close();\n\t\tthis.#session.close();\n\n\t\ttry {\n\t\t\tthis.#quic.close();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync #run(): Promise<void> {\n\t\ttry {\n\t\t\tawait Promise.all([this.#runBidis(), this.#runUnis(), this.#publisher.runPublishNamespaces()]);\n\t\t} catch (err) {\n\t\t\tif (!this.#closed) {\n\t\t\t\tconsole.error(\"fatal error running connection\", err);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\t/**\n\t * Publishes a broadcast to the connection.\n\t * @param name - The broadcast path to publish\n\t * @param broadcast - The broadcast to publish\n\t */\n\tpublish(path: Path.Valid, producer: broadcast.Producer) {\n\t\tthis.#publisher.publish(path, producer);\n\t}\n\n\t/**\n\t * Gets an announced reader for the specified prefix.\n\t * @param prefix - The prefix for announcements\n\t * @returns An Announced instance\n\t */\n\tannounced(prefix = Path.empty()): announce.Consumer {\n\t\treturn this.#subscriber.announced(prefix);\n\t}\n\n\t/**\n\t * Consumes a broadcast from the connection.\n\t *\n\t * @remarks\n\t * If the broadcast is not found, a \"not found\" error will be thrown when requesting any tracks.\n\t *\n\t * @param broadcast - The path of the broadcast to consume\n\t * @returns A Broadcast instance\n\t */\n\tconsume(path: Path.Valid): broadcast.Consumer {\n\t\treturn this.#subscriber.consume(path);\n\t}\n\n\t/**\n\t * Watches a broadcast, live only while it is announced.\n\t *\n\t * @param path - The path of the broadcast to watch\n\t * @returns A reactive handle to the broadcast\n\t */\n\tannouncedBroadcast(path: Path.Valid): announce.Broadcast {\n\t\treturn new announce.Broadcast({ connection: this, path });\n\t}\n\n\t/**\n\t * Accepts bidi streams (virtual for v14-v16, real for v17) and dispatches.\n\t */\n\tasync #runBidis() {\n\t\tfor (;;) {\n\t\t\tconst stream = await this.#session.acceptBi();\n\t\t\tif (!stream) break;\n\n\t\t\tvoid this.#runBidi(stream).catch((err: unknown) => {\n\t\t\t\tconsole.error(\"error processing bidi stream\", err);\n\t\t\t\tstream.abort(new Error(\"bidi stream error\"));\n\n\t\t\t\t// The peer broke the protocol, so losing the stream is not enough: nothing\n\t\t\t\t// stops it repeating the violation on the next one.\n\t\t\t\tif (err instanceof ProtocolViolation) this.close();\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Unified bidi stream dispatch. Reads typeId and routes to handler.\n\t * Matches the lite module's runBidi pattern.\n\t */\n\tasync #runBidi(stream: Stream) {\n\t\tconst typeId = await stream.reader.u53();\n\n\t\tswitch (typeId) {\n\t\t\t// Draft-18 SUBSCRIBE_NAMESPACE (0x50) and the legacy 0x11 message decode\n\t\t\t// to the same request_id + namespace; the legacy options field is ignored.\n\t\t\tcase SubscribeNamespace.id: {\n\t\t\t\tconst msg = await SubscribeNamespace.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runSubscribeNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SubscribeNamespaceLegacy.id: {\n\t\t\t\tconst legacy = await SubscribeNamespaceLegacy.decode(stream.reader, this.#session.version);\n\t\t\t\tconst msg = new SubscribeNamespace({ requestId: legacy.requestId, namespace: legacy.namespace });\n\t\t\t\tawait this.#publisher.runSubscribeNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SubscribeUpdate.id: {\n\t\t\t\t// REQUEST_UPDATE (0x02) is a follow-up, not a valid initial message\n\t\t\t\tstream.abort(new Error(\"unexpected REQUEST_UPDATE as initial message\"));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Publisher handles incoming requests\n\t\t\tcase Subscribe.id: {\n\t\t\t\tconst msg = await Subscribe.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runSubscribe(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase TrackStatusRequest.id: {\n\t\t\t\tconst msg = await TrackStatusRequest.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#publisher.runTrackStatusRequest(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Subscriber handles incoming notifications\n\t\t\tcase PublishNamespace.id: {\n\t\t\t\tconst msg = await PublishNamespace.decode(\n\t\t\t\t\tstream.reader,\n\t\t\t\t\tthis.#session.version,\n\t\t\t\t\tCluster.negotiated(this.#cluster),\n\t\t\t\t);\n\n\t\t\t\t// We always declare that advertisements to us must be solicited (MoQ\n\t\t\t\t// Solicit), and writing the option at all proves the peer implements the\n\t\t\t\t// extension, whichever value it chose. It also cannot have advertised\n\t\t\t\t// before reading our SETUP, since our SETUP is what says whether\n\t\t\t\t// advertising unasked is allowed. So this is a bug in the peer, and a\n\t\t\t\t// silent one on both sides if we tolerate it.\n\t\t\t\t//\n\t\t\t\t// Draft-14/15 are exempt: they have no inline NAMESPACE, so a\n\t\t\t\t// PUBLISH_NAMESPACE request is also how a peer answers our\n\t\t\t\t// SUBSCRIBE_NAMESPACE there, and the message alone does not say which.\n\t\t\t\tconst legacy = this.#session.version === Version.DRAFT_14 || this.#session.version === Version.DRAFT_15;\n\t\t\t\tif (this.#solicit !== undefined && !legacy) {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`unsolicited publish_namespace from a peer that implements MoQ Solicit: broadcast=${msg.trackNamespace}`,\n\t\t\t\t\t);\n\t\t\t\t\tthis.close();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tawait this.#subscriber.runPublishNamespace(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase Publish.id: {\n\t\t\t\tconst msg = await Publish.decode(stream.reader, this.#session.version);\n\t\t\t\tawait this.#subscriber.runPublish(msg, stream);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tconsole.warn(`unexpected bidi stream type: 0x${typeId.toString(16)}`);\n\t\t\t\tstream.abort(new Error(\"unexpected stream type\"));\n\t\t}\n\t}\n\n\t/**\n\t * Handles unidirectional streams for media delivery (groups).\n\t */\n\tasync #runUnis() {\n\t\tconst readers = new Readers(this.#quic, this.#session.version);\n\n\t\tfor (;;) {\n\t\t\tconst stream = await readers.next();\n\t\t\tif (!stream) break;\n\n\t\t\tthis.#runUni(stream)\n\t\t\t\t.then(() => {\n\t\t\t\t\tstream.stop(new Error(\"cancel\"));\n\t\t\t\t})\n\t\t\t\t.catch((err: unknown) => {\n\t\t\t\t\tconsole.error(\"error processing object stream\", err);\n\t\t\t\t\tstream.stop(err);\n\t\t\t\t});\n\t\t}\n\t}\n\n\tasync #runUni(stream: Reader) {\n\t\tconst header = await Group.decode(stream, this.#session.version);\n\t\tawait this.#subscriber.handleGroup(header, stream);\n\t}\n\n\t/**\n\t * v17+ only: reads GoAway from the setup/control stream.\n\t */\n\tasync #runGoAway(controlStream: Stream, version: IetfVersion) {\n\t\ttry {\n\t\t\tconst done = await controlStream.reader.done();\n\t\t\tif (done) return;\n\n\t\t\tconst typeId = await controlStream.reader.u53();\n\t\t\tif (typeId === GoAway.id) {\n\t\t\t\tconst msg = await GoAway.decode(controlStream.reader, version);\n\t\t\t\tconsole.warn(`received GOAWAY with redirect URI: ${msg.newSessionUri}`);\n\t\t\t} else {\n\t\t\t\tconsole.warn(`unexpected message on setup stream: 0x${typeId.toString(16)}`);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (!this.#closed) {\n\t\t\t\tconsole.error(\"error reading setup stream\", err);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.close();\n\t\t}\n\t}\n\n\t/**\n\t * Returns a promise that resolves when the connection is closed.\n\t * @returns A promise that resolves when closed\n\t */\n\tget closed(): Promise<void> {\n\t\treturn this.#quic.closed.then(() => undefined);\n\t}\n}\n"]}
package/ietf/fetch.d.ts CHANGED
@@ -1,6 +1,23 @@
1
1
  import type * as Path from "../path";
2
2
  import type { Reader, Writer } from "../stream";
3
3
  import type { IetfVersion } from "./version";
4
+ /**
5
+ * The header that begins a fetch stream (draft-20 section 11.4.4), naming the request it
6
+ * answers. Written by the publisher when serving a subscription's fill.
7
+ */
8
+ export declare class FetchHeader {
9
+ /** The uni stream type, not a control message id. */
10
+ static type: number;
11
+ /** The SUBSCRIBE (or FETCH) request this stream carries objects for. */
12
+ requestId: bigint;
13
+ constructor({ requestId }: {
14
+ requestId: bigint;
15
+ });
16
+ /** Write the header, which the stream type must already precede. */
17
+ encode(w: Writer, _version: IetfVersion): Promise<void>;
18
+ /** Read the header, with the stream type already consumed. */
19
+ static decode(r: Reader, _version: IetfVersion): Promise<FetchHeader>;
20
+ }
4
21
  export declare class Fetch {
5
22
  #private;
6
23
  static id: number;
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/ietf/fetch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/ietf/fetch.ts"],"names":[],"mappings":"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"}