@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.
- package/README.md +1 -0
- package/announced.d.ts +111 -21
- package/announced.d.ts.map +1 -1
- package/announced.js +213 -40
- package/announced.js.map +1 -1
- package/broadcast.d.ts +80 -25
- package/broadcast.d.ts.map +1 -1
- package/broadcast.js +270 -49
- package/broadcast.js.map +1 -1
- package/connection/accept.d.ts +6 -0
- package/connection/accept.d.ts.map +1 -1
- package/connection/accept.js +42 -13
- package/connection/accept.js.map +1 -1
- package/connection/browser.d.ts +5 -0
- package/connection/browser.d.ts.map +1 -0
- package/connection/browser.js +31 -0
- package/connection/browser.js.map +1 -0
- package/connection/connect.d.ts +43 -1
- package/connection/connect.d.ts.map +1 -1
- package/connection/connect.js +118 -123
- package/connection/connect.js.map +1 -1
- package/connection/established.d.ts +43 -16
- package/connection/established.d.ts.map +1 -1
- package/connection/established.js.map +1 -1
- package/connection/handshake.d.ts +12 -1
- package/connection/handshake.d.ts.map +1 -1
- package/connection/handshake.js +30 -14
- package/connection/handshake.js.map +1 -1
- package/connection/index.d.ts +8 -0
- package/connection/index.d.ts.map +1 -1
- package/connection/index.js +6 -0
- package/connection/index.js.map +1 -1
- package/connection/reload.d.ts +66 -9
- package/connection/reload.d.ts.map +1 -1
- package/connection/reload.js +192 -66
- package/connection/reload.js.map +1 -1
- package/connection/stats.d.ts +92 -0
- package/connection/stats.d.ts.map +1 -0
- package/connection/stats.js +35 -0
- package/connection/stats.js.map +1 -0
- package/connection/transport.d.ts +10 -0
- package/connection/transport.d.ts.map +1 -0
- package/connection/transport.js +12 -0
- package/connection/transport.js.map +1 -0
- package/consume.d.ts +44 -0
- package/consume.d.ts.map +1 -0
- package/consume.js +60 -0
- package/consume.js.map +1 -0
- package/datagram.d.ts +23 -0
- package/datagram.d.ts.map +1 -0
- package/datagram.js +3 -0
- package/datagram.js.map +1 -0
- package/error.d.ts +76 -0
- package/error.d.ts.map +1 -0
- package/error.js +107 -0
- package/error.js.map +1 -0
- package/group.d.ts +143 -38
- package/group.d.ts.map +1 -1
- package/group.js +270 -68
- package/group.js.map +1 -1
- package/ietf/adapter.d.ts +2 -2
- package/ietf/adapter.d.ts.map +1 -1
- package/ietf/adapter.js +20 -10
- package/ietf/adapter.js.map +1 -1
- package/ietf/aliases.d.ts +73 -5
- package/ietf/aliases.d.ts.map +1 -1
- package/ietf/aliases.js +121 -14
- package/ietf/aliases.js.map +1 -1
- package/ietf/cluster.d.ts +141 -0
- package/ietf/cluster.d.ts.map +1 -0
- package/ietf/cluster.js +204 -0
- package/ietf/cluster.js.map +1 -0
- package/ietf/connection.d.ts +39 -6
- package/ietf/connection.d.ts.map +1 -1
- package/ietf/connection.js +73 -12
- package/ietf/connection.js.map +1 -1
- package/ietf/fetch.d.ts +17 -0
- package/ietf/fetch.d.ts.map +1 -1
- package/ietf/fetch.js +21 -0
- package/ietf/fetch.js.map +1 -1
- package/ietf/filter.d.ts +87 -0
- package/ietf/filter.d.ts.map +1 -0
- package/ietf/filter.js +292 -0
- package/ietf/filter.js.map +1 -0
- package/ietf/index.d.ts +2 -0
- package/ietf/index.d.ts.map +1 -1
- package/ietf/index.js +3 -0
- package/ietf/index.js.map +1 -1
- package/ietf/namespace.d.ts +6 -0
- package/ietf/namespace.d.ts.map +1 -1
- package/ietf/namespace.js +27 -2
- package/ietf/namespace.js.map +1 -1
- package/ietf/object.d.ts +54 -6
- package/ietf/object.d.ts.map +1 -1
- package/ietf/object.js +178 -25
- package/ietf/object.js.map +1 -1
- package/ietf/parameters.d.ts +35 -10
- package/ietf/parameters.d.ts.map +1 -1
- package/ietf/parameters.js +236 -45
- package/ietf/parameters.js.map +1 -1
- package/ietf/priority.d.ts +5 -0
- package/ietf/priority.d.ts.map +1 -0
- package/ietf/priority.js +10 -0
- package/ietf/priority.js.map +1 -0
- package/ietf/properties.d.ts +13 -2
- package/ietf/properties.d.ts.map +1 -1
- package/ietf/properties.js +65 -8
- package/ietf/properties.js.map +1 -1
- package/ietf/publish.d.ts.map +1 -1
- package/ietf/publish.js +13 -3
- package/ietf/publish.js.map +1 -1
- package/ietf/publish_namespace.d.ts +16 -2
- package/ietf/publish_namespace.d.ts.map +1 -1
- package/ietf/publish_namespace.js +25 -6
- package/ietf/publish_namespace.js.map +1 -1
- package/ietf/publisher.d.ts +40 -7
- package/ietf/publisher.d.ts.map +1 -1
- package/ietf/publisher.js +751 -134
- package/ietf/publisher.js.map +1 -1
- package/ietf/request.js +1 -1
- package/ietf/request.js.map +1 -1
- package/ietf/solicit.d.ts +41 -0
- package/ietf/solicit.d.ts.map +1 -0
- package/ietf/solicit.js +47 -0
- package/ietf/solicit.js.map +1 -0
- package/ietf/subscribe.d.ts +41 -2
- package/ietf/subscribe.d.ts.map +1 -1
- package/ietf/subscribe.js +103 -22
- package/ietf/subscribe.js.map +1 -1
- package/ietf/subscribe_namespace.d.ts +21 -7
- package/ietf/subscribe_namespace.d.ts.map +1 -1
- package/ietf/subscribe_namespace.js +31 -14
- package/ietf/subscribe_namespace.js.map +1 -1
- package/ietf/subscriber.d.ts +20 -7
- package/ietf/subscriber.d.ts.map +1 -1
- package/ietf/subscriber.js +512 -142
- package/ietf/subscriber.js.map +1 -1
- package/ietf/version.d.ts +7 -1
- package/ietf/version.d.ts.map +1 -1
- package/ietf/version.js +7 -0
- package/ietf/version.js.map +1 -1
- package/index.d.ts +10 -5
- package/index.d.ts.map +1 -1
- package/index.js +10 -5
- package/index.js.map +1 -1
- package/internal.d.ts +29 -0
- package/internal.d.ts.map +1 -0
- package/internal.js +8 -0
- package/internal.js.map +1 -0
- package/lite/announce.d.ts +62 -15
- package/lite/announce.d.ts.map +1 -1
- package/lite/announce.js +230 -94
- package/lite/announce.js.map +1 -1
- package/lite/connection.d.ts +69 -20
- package/lite/connection.d.ts.map +1 -1
- package/lite/connection.js +128 -81
- package/lite/connection.js.map +1 -1
- package/lite/datagram.d.ts +23 -0
- package/lite/datagram.d.ts.map +1 -0
- package/lite/datagram.js +57 -0
- package/lite/datagram.js.map +1 -0
- package/lite/datagram_stream.d.ts +8 -0
- package/lite/datagram_stream.d.ts.map +1 -0
- package/lite/datagram_stream.js +44 -0
- package/lite/datagram_stream.js.map +1 -0
- package/lite/fetch.d.ts.map +1 -1
- package/lite/fetch.js +2 -2
- package/lite/fetch.js.map +1 -1
- package/lite/index.d.ts +3 -0
- package/lite/index.d.ts.map +1 -1
- package/lite/index.js +3 -0
- package/lite/index.js.map +1 -1
- package/lite/priority.d.ts +67 -0
- package/lite/priority.d.ts.map +1 -0
- package/lite/priority.js +114 -0
- package/lite/priority.js.map +1 -0
- package/lite/probe.d.ts +18 -2
- package/lite/probe.d.ts.map +1 -1
- package/lite/probe.js +24 -22
- package/lite/probe.js.map +1 -1
- package/lite/publisher.d.ts +14 -7
- package/lite/publisher.d.ts.map +1 -1
- package/lite/publisher.js +406 -84
- package/lite/publisher.js.map +1 -1
- package/lite/setup.d.ts +83 -6
- package/lite/setup.d.ts.map +1 -1
- package/lite/setup.js +203 -31
- package/lite/setup.js.map +1 -1
- package/lite/stream.d.ts +12 -3
- package/lite/stream.d.ts.map +1 -1
- package/lite/stream.js +7 -4
- package/lite/stream.js.map +1 -1
- package/lite/subscribe.d.ts +37 -23
- package/lite/subscribe.d.ts.map +1 -1
- package/lite/subscribe.js +70 -55
- package/lite/subscribe.js.map +1 -1
- package/lite/subscriber.d.ts +29 -23
- package/lite/subscriber.d.ts.map +1 -1
- package/lite/subscriber.js +689 -112
- package/lite/subscriber.js.map +1 -1
- package/lite/track.d.ts +25 -13
- package/lite/track.d.ts.map +1 -1
- package/lite/track.js +60 -35
- package/lite/track.js.map +1 -1
- package/lite/version.d.ts +39 -4
- package/lite/version.d.ts.map +1 -1
- package/lite/version.js +139 -17
- package/lite/version.js.map +1 -1
- package/mock.d.ts +53 -3
- package/mock.d.ts.map +1 -1
- package/mock.js +110 -21
- package/mock.js.map +1 -1
- package/{lite/origin.d.ts → origin.d.ts} +25 -1
- package/origin.d.ts.map +1 -0
- package/{lite/origin.js → origin.js} +25 -1
- package/origin.js.map +1 -0
- package/package.json +6 -4
- package/path.d.ts +67 -0
- package/path.d.ts.map +1 -1
- package/path.js +128 -0
- package/path.js.map +1 -1
- package/stream.d.ts +94 -4
- package/stream.d.ts.map +1 -1
- package/stream.js +128 -17
- package/stream.js.map +1 -1
- package/time.d.ts +109 -86
- package/time.d.ts.map +1 -1
- package/time.js +80 -30
- package/time.js.map +1 -1
- package/track.d.ts +298 -47
- package/track.d.ts.map +1 -1
- package/track.js +721 -109
- package/track.js.map +1 -1
- package/util/timeout.d.ts +5 -0
- package/util/timeout.d.ts.map +1 -0
- package/util/timeout.js +18 -0
- package/util/timeout.js.map +1 -0
- package/util/utf8.d.ts +3 -0
- package/util/utf8.d.ts.map +1 -0
- package/util/utf8.js +7 -0
- package/util/utf8.js.map +1 -0
- package/varint.d.ts +14 -3
- package/varint.d.ts.map +1 -1
- package/varint.js +23 -12
- package/varint.js.map +1 -1
- package/zod.d.ts +5 -5
- package/zod.d.ts.map +1 -1
- package/zod.js.map +1 -1
- package/bandwidth.d.ts +0 -11
- package/bandwidth.d.ts.map +0 -1
- package/bandwidth.js +0 -7
- package/bandwidth.js.map +0 -1
- package/connection/cert.test.d.ts +0 -2
- package/connection/cert.test.d.ts.map +0 -1
- package/connection/cert.test.js +0 -26
- package/connection/cert.test.js.map +0 -1
- package/connection/reload.test.d.ts +0 -2
- package/connection/reload.test.d.ts.map +0 -1
- package/connection/reload.test.js +0 -39
- package/connection/reload.test.js.map +0 -1
- package/group.test.d.ts +0 -2
- package/group.test.d.ts.map +0 -1
- package/group.test.js +0 -66
- package/group.test.js.map +0 -1
- package/ietf/aliases.test.d.ts +0 -2
- package/ietf/aliases.test.d.ts.map +0 -1
- package/ietf/aliases.test.js +0 -30
- package/ietf/aliases.test.js.map +0 -1
- package/ietf/ietf.test.d.ts +0 -2
- package/ietf/ietf.test.d.ts.map +0 -1
- package/ietf/ietf.test.js +0 -873
- package/ietf/ietf.test.js.map +0 -1
- package/integration.test.d.ts +0 -2
- package/integration.test.d.ts.map +0 -1
- package/integration.test.js +0 -90
- package/integration.test.js.map +0 -1
- package/lite/origin.d.ts.map +0 -1
- package/lite/origin.js.map +0 -1
- package/lite/subscriber.test.d.ts +0 -2
- package/lite/subscriber.test.d.ts.map +0 -1
- package/lite/subscriber.test.js +0 -30
- package/lite/subscriber.test.js.map +0 -1
- package/lite/track.test.d.ts +0 -2
- package/lite/track.test.d.ts.map +0 -1
- package/lite/track.test.js +0 -53
- package/lite/track.test.js.map +0 -1
- package/path.test.d.ts +0 -2
- package/path.test.d.ts.map +0 -1
- package/path.test.js +0 -169
- package/path.test.js.map +0 -1
- package/stream.test.d.ts +0 -2
- package/stream.test.d.ts.map +0 -1
- package/stream.test.js +0 -210
- package/stream.test.js.map +0 -1
- package/track.test.d.ts +0 -2
- package/track.test.d.ts.map +0 -1
- package/track.test.js +0 -56
- package/track.test.js.map +0 -1
- package/util/error.d.ts +0 -3
- package/util/error.d.ts.map +0 -1
- package/util/error.js +0 -9
- package/util/error.js.map +0 -1
- package/util/index.d.ts +0 -2
- package/util/index.d.ts.map +0 -1
- package/util/index.js +0 -3
- package/util/index.js.map +0 -1
- package/varint.test.d.ts +0 -2
- package/varint.test.d.ts.map +0 -1
- package/varint.test.js +0 -106
- package/varint.test.js.map +0 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* @ts-self-types="./browser.d.ts" */
|
|
2
|
+
import Bowser from "bowser";
|
|
3
|
+
/** Returns whether a browser user agent has a usable WebTransport implementation. */
|
|
4
|
+
export function isWebTransportUserAgentSupported(userAgent) {
|
|
5
|
+
const browser = Bowser.getParser(userAgent);
|
|
6
|
+
const supported = browser.satisfies({
|
|
7
|
+
// Fixed with 153.0.0, Firefox only allows two concurrent remote-initiated streams:
|
|
8
|
+
// https://bugzilla.mozilla.org/show_bug.cgi?id=2046262
|
|
9
|
+
firefox: ">=153.0",
|
|
10
|
+
// Safari's flow-control window never refills, which permanently stalls sessions:
|
|
11
|
+
// https://bugs.webkit.org/show_bug.cgi?id=319818
|
|
12
|
+
// It also stops delivering incoming unidirectional streams for good after
|
|
13
|
+
// roughly 7,600 of them or 16 MiB on one session, whichever comes first
|
|
14
|
+
// (moq-dev/moq#2388); one stream per group reaches that in about two minutes.
|
|
15
|
+
safari: "<0",
|
|
16
|
+
});
|
|
17
|
+
if (supported === undefined) {
|
|
18
|
+
//By default, other browsers are considered to support WebTransport.
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return supported;
|
|
22
|
+
}
|
|
23
|
+
/** Returns whether this runtime can connect with WebTransport. */
|
|
24
|
+
export function isWebTransportSupported() {
|
|
25
|
+
if (typeof globalThis.WebTransport === "undefined")
|
|
26
|
+
return false;
|
|
27
|
+
if (typeof navigator === "undefined")
|
|
28
|
+
return true;
|
|
29
|
+
return isWebTransportUserAgentSupported(navigator.userAgent);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/connection/browser.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,qFAAqF;AACrF,MAAM,UAAU,gCAAgC,CAAC,SAAiB;IACjE,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,mFAAmF;QACnF,uDAAuD;QACvD,OAAO,EAAE,SAAS;QAElB,iFAAiF;QACjF,iDAAiD;QACjD,0EAA0E;QAC1E,wEAAwE;QACxE,8EAA8E;QAC9E,MAAM,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,oEAAoE;QACpE,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,uBAAuB;IACtC,IAAI,OAAO,UAAU,CAAC,YAAY,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACjE,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,gCAAgC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import Bowser from \"bowser\";\n\n/** Returns whether a browser user agent has a usable WebTransport implementation. */\nexport function isWebTransportUserAgentSupported(userAgent: string): boolean {\n\tconst browser = Bowser.getParser(userAgent);\n\tconst supported = browser.satisfies({\n\t\t// Fixed with 153.0.0, Firefox only allows two concurrent remote-initiated streams:\n\t\t// https://bugzilla.mozilla.org/show_bug.cgi?id=2046262\n\t\tfirefox: \">=153.0\",\n\n\t\t// Safari's flow-control window never refills, which permanently stalls sessions:\n\t\t// https://bugs.webkit.org/show_bug.cgi?id=319818\n\t\t// It also stops delivering incoming unidirectional streams for good after\n\t\t// roughly 7,600 of them or 16 MiB on one session, whichever comes first\n\t\t// (moq-dev/moq#2388); one stream per group reaches that in about two minutes.\n\t\tsafari: \"<0\",\n\t});\n\n\tif (supported === undefined) {\n\t\t//By default, other browsers are considered to support WebTransport.\n\t\treturn true;\n\t}\n\treturn supported;\n}\n\n/** Returns whether this runtime can connect with WebTransport. */\nexport function isWebTransportSupported(): boolean {\n\tif (typeof globalThis.WebTransport === \"undefined\") return false;\n\tif (typeof navigator === \"undefined\") return true;\n\treturn isWebTransportUserAgentSupported(navigator.userAgent);\n}\n"]}
|
package/connection/connect.d.ts
CHANGED
|
@@ -1,30 +1,72 @@
|
|
|
1
1
|
import type { Established } from "./established";
|
|
2
2
|
/** Tuning for the WebSocket fallback used when WebTransport is unavailable or loses the connect race. */
|
|
3
3
|
export interface WebSocketOptions {
|
|
4
|
+
/** Enable the WebSocket fallback. Defaults to `true`. */
|
|
4
5
|
enabled?: boolean;
|
|
6
|
+
/** Use a different URL than WebTransport. By default, `https` maps to `wss` and `http` to `ws`. */
|
|
5
7
|
url?: URL;
|
|
8
|
+
/**
|
|
9
|
+
* The delay in milliseconds before attempting the WebSocket fallback (default: 500).
|
|
10
|
+
* If WebSocket won the previous race for a given URL, this is 0.
|
|
11
|
+
*/
|
|
6
12
|
delay?: DOMHighResTimeStamp;
|
|
7
13
|
}
|
|
8
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* A server certificate hash used to pin a self-signed server, one entry of
|
|
16
|
+
* `serverCertificateHashes`. Unlike the DOM type, `value` also accepts a hex string (the
|
|
17
|
+
* format moq servers report via their certificate fingerprints), decoded automatically.
|
|
18
|
+
*/
|
|
9
19
|
export interface CertificateHash {
|
|
20
|
+
/** The hash algorithm. Defaults to `sha-256` (the only supported value). */
|
|
10
21
|
algorithm?: "sha-256";
|
|
22
|
+
/** The certificate hash: raw bytes or a hex string. */
|
|
11
23
|
value: BufferSource | string;
|
|
12
24
|
}
|
|
13
25
|
/** WebTransport options extended with friendlier certificate pinning (hex hashes or a raw certificate). */
|
|
14
26
|
export interface WebTransportProps extends Omit<WebTransportOptions, "serverCertificateHashes"> {
|
|
27
|
+
/**
|
|
28
|
+
* Pin the server to one or more certificate hashes. Each `value` may be raw
|
|
29
|
+
* bytes or a hex string; the algorithm defaults to `sha-256`.
|
|
30
|
+
*/
|
|
15
31
|
serverCertificateHashes?: CertificateHash[];
|
|
32
|
+
/**
|
|
33
|
+
* Pin the server by supplying its certificate directly; the SHA-256 hash is
|
|
34
|
+
* computed for you. Accepts a PEM string or raw DER bytes. Use this when you
|
|
35
|
+
* have the certificate but not its precomputed fingerprint.
|
|
36
|
+
*/
|
|
16
37
|
serverCertificate?: string | BufferSource;
|
|
17
38
|
}
|
|
18
39
|
/** Options for {@link connect}. */
|
|
19
40
|
export interface ConnectProps {
|
|
41
|
+
/** WebTransport options. */
|
|
20
42
|
webtransport?: WebTransportProps;
|
|
43
|
+
/** WebSocket (fallback) options. */
|
|
21
44
|
websocket?: WebSocketOptions;
|
|
45
|
+
/**
|
|
46
|
+
* Use a pre-existing WebTransport session instead of connecting; skips the
|
|
47
|
+
* WebTransport/WebSocket race. The publisher acquires the session's datagram
|
|
48
|
+
* writer lock (`datagrams.writable.getWriter()`) for the session's lifetime,
|
|
49
|
+
* so the caller must not hold it. Aborting via {@link ConnectProps.signal}
|
|
50
|
+
* closes this session, so don't share one you intend to reuse afterwards.
|
|
51
|
+
*/
|
|
22
52
|
transport?: WebTransport;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the relay supports broadcast discovery; see {@link Established.discovery}.
|
|
55
|
+
* Defaults to true, except for relays known to lack it.
|
|
56
|
+
*/
|
|
57
|
+
discovery?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Aborts the connection attempt with the signal's reason. An already-aborted
|
|
60
|
+
* signal rejects before anything opens, and aborting after the connection is
|
|
61
|
+
* established has no effect. Use `AbortSignal.timeout(ms)` for a deadline.
|
|
62
|
+
*/
|
|
63
|
+
signal?: AbortSignal;
|
|
23
64
|
}
|
|
24
65
|
/**
|
|
25
66
|
* Establishes a connection to a MOQ server.
|
|
26
67
|
*
|
|
27
68
|
* @param url - The URL of the server to connect to
|
|
69
|
+
* @param props - Connection options
|
|
28
70
|
* @returns A promise that resolves to a Connection instance
|
|
29
71
|
*/
|
|
30
72
|
export declare function connect(url: URL, props?: ConnectProps): Promise<Established>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/connection/connect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/connection/connect.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,yGAAyG;AACzG,MAAM,WAAW,gBAAgB;IAChC,yDAAyD;IACzD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,mGAAmG;IACnG,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uDAAuD;IACvD,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC;CAC7B;AAED,2GAA2G;AAC3G,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;IAC9F;;;OAGG;IACH,uBAAuB,CAAC,EAAE,eAAe,EAAE,CAAC;IAE5C;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;CAC1C;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC5B,4BAA4B;IAC5B,YAAY,CAAC,EAAE,iBAAiB,CAAC;IAEjC,oCAAoC;IACpC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,YAAY,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAkBD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAqBlF;AAkMD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAInG"}
|
package/connection/connect.js
CHANGED
|
@@ -4,28 +4,61 @@ import * as Ietf from "../ietf/index.js";
|
|
|
4
4
|
import * as Lite from "../lite/index.js";
|
|
5
5
|
import { Stream } from "../stream.js";
|
|
6
6
|
import * as Hex from "../util/hex.js";
|
|
7
|
+
import { isWebTransportSupported } from "./browser.js";
|
|
7
8
|
import { exchangeSetup } from "./handshake.js";
|
|
8
9
|
// Default head start for WebTransport before attempting the WebSocket fallback.
|
|
9
10
|
const DEFAULT_WEBSOCKET_DELAY_MS = 500;
|
|
11
|
+
// Relays that don't implement broadcast discovery (SUBSCRIBE_NAMESPACE), so `announced()` would
|
|
12
|
+
// never yield and a consumer waiting on an announcement would hang forever. Override with the
|
|
13
|
+
// `discovery` option. Drop a host once its relay ships discovery.
|
|
14
|
+
const NO_DISCOVERY_HOSTS = ["mediaoverquic.com"];
|
|
15
|
+
/** Whether the relay at `url` is expected to support broadcast discovery. */
|
|
16
|
+
function defaultDiscovery(url) {
|
|
17
|
+
return !NO_DISCOVERY_HOSTS.some((host) => url.hostname.endsWith(host));
|
|
18
|
+
}
|
|
10
19
|
// Save if WebSocket won the last race, so we won't give QUIC a head start next time.
|
|
11
20
|
const websocketWon = new Set();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// TODO: remove once Firefox fixes incoming bidi delivery.
|
|
15
|
-
const isFirefox = typeof navigator !== "undefined" && navigator.userAgent.toLowerCase().includes("firefox");
|
|
21
|
+
/** The default connect signal: never aborts. */
|
|
22
|
+
const NEVER_ABORTED = new AbortController().signal;
|
|
16
23
|
/**
|
|
17
24
|
* Establishes a connection to a MOQ server.
|
|
18
25
|
*
|
|
19
26
|
* @param url - The URL of the server to connect to
|
|
27
|
+
* @param props - Connection options
|
|
20
28
|
* @returns A promise that resolves to a Connection instance
|
|
21
29
|
*/
|
|
22
30
|
export async function connect(url, props) {
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
const signal = props?.signal ?? NEVER_ABORTED;
|
|
32
|
+
signal.throwIfAborted();
|
|
33
|
+
// Resolves on abort so every in-flight transport tears itself down.
|
|
34
|
+
const { promise: abort, resolve } = Promise.withResolvers();
|
|
35
|
+
const onAbort = () => resolve();
|
|
36
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
37
|
+
const pending = connectInner(url, props, abort);
|
|
38
|
+
try {
|
|
39
|
+
// A `pending` rejection propagates unless the abort beat it to the finish line.
|
|
40
|
+
const connection = await Promise.race([pending, abort.then(() => undefined)]);
|
|
41
|
+
if (connection && !signal.aborted)
|
|
42
|
+
return connection;
|
|
43
|
+
// Close a connection that settles after the abort.
|
|
44
|
+
pending.then((conn) => conn.close()).catch(() => { });
|
|
45
|
+
throw signal.reason;
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
signal.removeEventListener("abort", onAbort);
|
|
25
49
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
50
|
+
}
|
|
51
|
+
async function connectInner(url, props, abort) {
|
|
52
|
+
const discovery = props?.discovery ?? defaultDiscovery(url);
|
|
53
|
+
if (props?.transport) {
|
|
54
|
+
const transport = props.transport;
|
|
55
|
+
void abort.then(() => transport.close());
|
|
56
|
+
return connectTransport(url, transport, discovery);
|
|
57
|
+
}
|
|
58
|
+
// Stop transports after one connects or the caller aborts.
|
|
59
|
+
const { promise: raced, resolve: done } = Promise.withResolvers();
|
|
60
|
+
const cancel = Promise.race([raced, abort]);
|
|
61
|
+
const webtransport = isWebTransportSupported() ? connectWebTransport(url, cancel, props?.webtransport) : undefined;
|
|
29
62
|
// Give QUIC a head start to connect before trying WebSocket, unless WebSocket has won in the past.
|
|
30
63
|
// NOTE that QUIC should be faster because it involves 1/2 fewer RTTs.
|
|
31
64
|
const headstart = !webtransport || websocketWon.has(url.toString()) ? 0 : (props?.websocket?.delay ?? DEFAULT_WEBSOCKET_DELAY_MS);
|
|
@@ -46,32 +79,37 @@ export async function connect(url, props) {
|
|
|
46
79
|
done();
|
|
47
80
|
if (!session)
|
|
48
81
|
throw new Error("no transport available");
|
|
82
|
+
// Abort the setup handshake without leaking the selected transport.
|
|
83
|
+
void abort.then(() => session.close());
|
|
49
84
|
// Save if WebSocket won the last race, so we won't give QUIC a head start next time.
|
|
50
85
|
if (session instanceof Session) {
|
|
51
86
|
console.warn(url.toString(), "connected via WebSocket");
|
|
52
87
|
websocketWon.add(url.toString());
|
|
53
88
|
}
|
|
54
89
|
else {
|
|
55
|
-
console.
|
|
90
|
+
console.debug(url.toString(), "connected via WebTransport");
|
|
56
91
|
}
|
|
57
|
-
//
|
|
92
|
+
// The remaining setup is identical whether the transport was raced or supplied.
|
|
93
|
+
return await connectTransport(url, session, discovery);
|
|
94
|
+
}
|
|
95
|
+
async function connectTransport(url, session, discovery) {
|
|
96
|
+
// qmux Session exposes the negotiated protocol directly (as "" when there is none);
|
|
58
97
|
// native WebTransport doesn't have a standard .protocol property yet.
|
|
59
|
-
const protocol = session
|
|
60
|
-
? session.protocol || undefined
|
|
61
|
-
: // @ts-expect-error - TODO: add protocol to WebTransport
|
|
62
|
-
session.protocol;
|
|
98
|
+
const protocol = session.protocol || undefined;
|
|
63
99
|
console.debug(url.toString(), "negotiated ALPN:", protocol ?? "(none)");
|
|
64
100
|
// Choose setup encoding based on negotiated WebTransport protocol (if any).
|
|
65
101
|
let setupVersion;
|
|
66
|
-
const modernVersion = protocol === Ietf.ALPN.
|
|
67
|
-
? Ietf.Version.
|
|
68
|
-
: protocol === Ietf.ALPN.
|
|
69
|
-
? Ietf.Version.
|
|
70
|
-
: protocol === Ietf.ALPN.
|
|
71
|
-
? Ietf.Version.
|
|
72
|
-
:
|
|
102
|
+
const modernVersion = protocol === Ietf.ALPN.DRAFT_20
|
|
103
|
+
? Ietf.Version.DRAFT_20
|
|
104
|
+
: protocol === Ietf.ALPN.DRAFT_19
|
|
105
|
+
? Ietf.Version.DRAFT_19
|
|
106
|
+
: protocol === Ietf.ALPN.DRAFT_18
|
|
107
|
+
? Ietf.Version.DRAFT_18
|
|
108
|
+
: protocol === Ietf.ALPN.DRAFT_17
|
|
109
|
+
? Ietf.Version.DRAFT_17
|
|
110
|
+
: undefined;
|
|
73
111
|
if (modernVersion !== undefined) {
|
|
74
|
-
return await handshakeAlpn(url, session, modernVersion);
|
|
112
|
+
return await handshakeAlpn(url, session, modernVersion, discovery);
|
|
75
113
|
}
|
|
76
114
|
else if (protocol === Ietf.ALPN.DRAFT_16) {
|
|
77
115
|
setupVersion = Ietf.Version.DRAFT_16;
|
|
@@ -79,37 +117,32 @@ export async function connect(url, props) {
|
|
|
79
117
|
else if (protocol === Ietf.ALPN.DRAFT_15) {
|
|
80
118
|
setupVersion = Ietf.Version.DRAFT_15;
|
|
81
119
|
}
|
|
82
|
-
else if (protocol === Lite.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
120
|
+
else if (protocol === Lite.ALPN_06_WIP) {
|
|
121
|
+
return new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_06, discovery });
|
|
122
|
+
}
|
|
123
|
+
else if (protocol === Lite.ALPN_05) {
|
|
124
|
+
return new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_05, discovery });
|
|
86
125
|
}
|
|
87
126
|
else if (protocol === Lite.ALPN_04) {
|
|
88
|
-
|
|
89
|
-
return new Lite.Connection(url, session, Lite.Version.DRAFT_04, undefined);
|
|
127
|
+
return new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_04, discovery });
|
|
90
128
|
}
|
|
91
129
|
else if (protocol === Lite.ALPN_03) {
|
|
92
|
-
|
|
93
|
-
return new Lite.Connection(url, session, Lite.Version.DRAFT_03, undefined);
|
|
130
|
+
return new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_03, discovery });
|
|
94
131
|
}
|
|
95
132
|
else if (protocol === Lite.ALPN || protocol === "" || protocol === undefined) {
|
|
96
|
-
// moq-lite ALPN (or no protocol) uses Draft14 encoding for SETUP,
|
|
97
|
-
// then negotiates the actual version via the SETUP message.
|
|
98
133
|
setupVersion = Ietf.Version.DRAFT_14;
|
|
99
134
|
}
|
|
100
135
|
else {
|
|
101
136
|
throw new Error(`unsupported WebTransport protocol: ${protocol}`);
|
|
102
137
|
}
|
|
103
|
-
// We're encoding 0x20 so it's backwards compatible with moq-transport-10+
|
|
104
138
|
const stream = await Stream.open(session);
|
|
105
139
|
await stream.writer.u53(Lite.StreamId.ClientCompat);
|
|
106
140
|
const encoder = new TextEncoder();
|
|
107
141
|
const params = new Ietf.SetupOptions();
|
|
108
|
-
params.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);
|
|
109
|
-
params.setBytes(Ietf.SetupOption.Implementation, encoder.encode("moq-lite-js"));
|
|
142
|
+
params.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);
|
|
143
|
+
params.setBytes(Ietf.SetupOption.Implementation, encoder.encode("moq-lite-js"));
|
|
144
|
+
Ietf.solicitIntoSetup(params);
|
|
110
145
|
const client = new Ietf.ClientSetup({
|
|
111
|
-
// NOTE: draft 15 onwards does not use CLIENT_SETUP to negotiate the version.
|
|
112
|
-
// We still echo it just to make sure we're not accidentally trying to negotiate the version.
|
|
113
146
|
versions: setupVersion === Ietf.Version.DRAFT_16
|
|
114
147
|
? [Ietf.Version.DRAFT_16]
|
|
115
148
|
: setupVersion === Ietf.Version.DRAFT_15
|
|
@@ -117,100 +150,32 @@ export async function connect(url, props) {
|
|
|
117
150
|
: [Lite.Version.DRAFT_02, Lite.Version.DRAFT_01, Ietf.Version.DRAFT_14],
|
|
118
151
|
parameters: params,
|
|
119
152
|
});
|
|
120
|
-
console.debug(url.toString(), "sending client setup", client);
|
|
121
153
|
await client.encode(stream.writer, setupVersion);
|
|
122
|
-
// And we expect 0x21 as the response.
|
|
123
154
|
const serverCompat = await stream.reader.u53();
|
|
124
155
|
if (serverCompat !== Lite.StreamId.ServerCompat) {
|
|
125
156
|
throw new Error(`unsupported server message type: ${serverCompat.toString()}`);
|
|
126
157
|
}
|
|
127
|
-
// Decode ServerSetup in Draft14 format (version + params)
|
|
128
158
|
const server = await Ietf.ServerSetup.decode(stream.reader, setupVersion);
|
|
129
|
-
console.debug(url.toString(), "received server setup", server);
|
|
130
159
|
if (Object.values(Lite.Version).includes(server.version)) {
|
|
131
|
-
return new Lite.Connection(
|
|
132
|
-
}
|
|
133
|
-
else if (Object.values(Ietf.Version).includes(server.version)) {
|
|
134
|
-
const maxRequestId = server.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;
|
|
135
|
-
return new Ietf.Connection({
|
|
160
|
+
return new Lite.Connection({
|
|
136
161
|
url,
|
|
137
162
|
quic: session,
|
|
138
|
-
control: stream,
|
|
139
|
-
maxRequestId,
|
|
140
163
|
version: server.version,
|
|
164
|
+
session: stream,
|
|
165
|
+
discovery,
|
|
141
166
|
});
|
|
142
167
|
}
|
|
143
|
-
else {
|
|
144
|
-
throw new Error(`unsupported server version: ${server.version.toString()}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async function connectTransport(url, session) {
|
|
148
|
-
// @ts-expect-error - TODO: add protocol to WebTransport
|
|
149
|
-
const protocol = session.protocol;
|
|
150
|
-
// Choose setup encoding based on negotiated WebTransport protocol (if any).
|
|
151
|
-
let setupVersion;
|
|
152
|
-
const modernVersion = protocol === Ietf.ALPN.DRAFT_19
|
|
153
|
-
? Ietf.Version.DRAFT_19
|
|
154
|
-
: protocol === Ietf.ALPN.DRAFT_18
|
|
155
|
-
? Ietf.Version.DRAFT_18
|
|
156
|
-
: protocol === Ietf.ALPN.DRAFT_17
|
|
157
|
-
? Ietf.Version.DRAFT_17
|
|
158
|
-
: undefined;
|
|
159
|
-
if (modernVersion !== undefined) {
|
|
160
|
-
return await handshakeAlpn(url, session, modernVersion);
|
|
161
|
-
}
|
|
162
|
-
else if (protocol === Ietf.ALPN.DRAFT_16) {
|
|
163
|
-
setupVersion = Ietf.Version.DRAFT_16;
|
|
164
|
-
}
|
|
165
|
-
else if (protocol === Ietf.ALPN.DRAFT_15) {
|
|
166
|
-
setupVersion = Ietf.Version.DRAFT_15;
|
|
167
|
-
}
|
|
168
|
-
else if (protocol === Lite.ALPN_05_WIP) {
|
|
169
|
-
return new Lite.Connection(url, session, Lite.Version.DRAFT_05_WIP, undefined);
|
|
170
|
-
}
|
|
171
|
-
else if (protocol === Lite.ALPN_04) {
|
|
172
|
-
return new Lite.Connection(url, session, Lite.Version.DRAFT_04, undefined);
|
|
173
|
-
}
|
|
174
|
-
else if (protocol === Lite.ALPN_03) {
|
|
175
|
-
return new Lite.Connection(url, session, Lite.Version.DRAFT_03, undefined);
|
|
176
|
-
}
|
|
177
|
-
else if (protocol === Lite.ALPN || protocol === "" || protocol === undefined) {
|
|
178
|
-
setupVersion = Ietf.Version.DRAFT_14;
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
throw new Error(`unsupported WebTransport protocol: ${protocol}`);
|
|
182
|
-
}
|
|
183
|
-
const stream = await Stream.open(session);
|
|
184
|
-
await stream.writer.u53(Lite.StreamId.ClientCompat);
|
|
185
|
-
const encoder = new TextEncoder();
|
|
186
|
-
const params = new Ietf.SetupOptions();
|
|
187
|
-
params.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);
|
|
188
|
-
params.setBytes(Ietf.SetupOption.Implementation, encoder.encode("moq-lite-js"));
|
|
189
|
-
const client = new Ietf.ClientSetup({
|
|
190
|
-
versions: setupVersion === Ietf.Version.DRAFT_16
|
|
191
|
-
? [Ietf.Version.DRAFT_16]
|
|
192
|
-
: setupVersion === Ietf.Version.DRAFT_15
|
|
193
|
-
? [Ietf.Version.DRAFT_15]
|
|
194
|
-
: [Lite.Version.DRAFT_02, Lite.Version.DRAFT_01, Ietf.Version.DRAFT_14],
|
|
195
|
-
parameters: params,
|
|
196
|
-
});
|
|
197
|
-
await client.encode(stream.writer, setupVersion);
|
|
198
|
-
const serverCompat = await stream.reader.u53();
|
|
199
|
-
if (serverCompat !== Lite.StreamId.ServerCompat) {
|
|
200
|
-
throw new Error(`unsupported server message type: ${serverCompat.toString()}`);
|
|
201
|
-
}
|
|
202
|
-
const server = await Ietf.ServerSetup.decode(stream.reader, setupVersion);
|
|
203
|
-
if (Object.values(Lite.Version).includes(server.version)) {
|
|
204
|
-
return new Lite.Connection(url, session, server.version, stream);
|
|
205
|
-
}
|
|
206
168
|
else if (Object.values(Ietf.Version).includes(server.version)) {
|
|
207
169
|
const maxRequestId = server.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;
|
|
208
170
|
return new Ietf.Connection({
|
|
171
|
+
discovery,
|
|
172
|
+
client: true,
|
|
209
173
|
url,
|
|
210
174
|
quic: session,
|
|
211
175
|
control: stream,
|
|
212
176
|
maxRequestId,
|
|
213
177
|
version: server.version,
|
|
178
|
+
solicit: Ietf.solicitFromSetup(server.parameters),
|
|
214
179
|
});
|
|
215
180
|
}
|
|
216
181
|
else {
|
|
@@ -221,12 +186,16 @@ async function connectTransport(url, session) {
|
|
|
221
186
|
* Draft-17+ client handshake. ALPN already pinned the version; SETUP is
|
|
222
187
|
* exchanged over a pair of uni streams using stream type 0x2F00.
|
|
223
188
|
*/
|
|
224
|
-
async function handshakeAlpn(url, session, version) {
|
|
225
|
-
const
|
|
189
|
+
async function handshakeAlpn(url, session, version, discovery) {
|
|
190
|
+
const { control, solicit, cluster } = await exchangeSetup(session, version, "moq-lite-js");
|
|
226
191
|
return new Ietf.Connection({
|
|
192
|
+
discovery,
|
|
193
|
+
client: true,
|
|
227
194
|
url,
|
|
228
195
|
quic: session,
|
|
229
|
-
control
|
|
196
|
+
control,
|
|
197
|
+
solicit,
|
|
198
|
+
cluster,
|
|
230
199
|
// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.
|
|
231
200
|
maxRequestId: 0n,
|
|
232
201
|
version,
|
|
@@ -275,16 +244,18 @@ async function connectWebTransport(url, cancel, options) {
|
|
|
275
244
|
allowPooling: false,
|
|
276
245
|
congestionControl: "low-latency",
|
|
277
246
|
protocols: [
|
|
247
|
+
// Lite.ALPN_06_WIP is intentionally omitted: lite-06 is work-in-progress and
|
|
248
|
+
// not advertised by default (connect.ts still accepts it if a server negotiates it).
|
|
249
|
+
Lite.ALPN_05,
|
|
278
250
|
Lite.ALPN_04,
|
|
279
251
|
Lite.ALPN_03,
|
|
280
252
|
Lite.ALPN,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
// which norsk serves fine. Restore these once net draft-16 works.
|
|
253
|
+
Ietf.ALPN.DRAFT_20,
|
|
254
|
+
Ietf.ALPN.DRAFT_19,
|
|
255
|
+
Ietf.ALPN.DRAFT_18,
|
|
256
|
+
Ietf.ALPN.DRAFT_17,
|
|
257
|
+
Ietf.ALPN.DRAFT_16,
|
|
258
|
+
Ietf.ALPN.DRAFT_15,
|
|
288
259
|
],
|
|
289
260
|
...webtransport,
|
|
290
261
|
};
|
|
@@ -334,7 +305,31 @@ async function connectWebSocket(url, delay, cancel) {
|
|
|
334
305
|
const active = await Promise.race([cancel, timer.then(() => true)]);
|
|
335
306
|
if (!active)
|
|
336
307
|
return undefined;
|
|
337
|
-
|
|
308
|
+
// Only moq-transport-18 is pinned to qmux-01 today. Every other ALPN we
|
|
309
|
+
// support is currently negotiated as `qmux-00.{alpn}` on the wire, but we
|
|
310
|
+
// don't want to lock that in: set the value to `null` so the polyfill
|
|
311
|
+
// advertises every QMux draft it knows about and the server picks one.
|
|
312
|
+
// Insertion order is the negotiation preference on the wire.
|
|
313
|
+
const versions = {
|
|
314
|
+
// Lite.ALPN_06_WIP omitted on purpose: lite-06 is work-in-progress, not advertised by default.
|
|
315
|
+
[Lite.ALPN_05]: null,
|
|
316
|
+
[Lite.ALPN_04]: null,
|
|
317
|
+
[Lite.ALPN_03]: null,
|
|
318
|
+
[Lite.ALPN]: null,
|
|
319
|
+
[Ietf.ALPN.DRAFT_20]: "qmux-01",
|
|
320
|
+
[Ietf.ALPN.DRAFT_18]: "qmux-01",
|
|
321
|
+
[Ietf.ALPN.DRAFT_17]: null,
|
|
322
|
+
[Ietf.ALPN.DRAFT_16]: null,
|
|
323
|
+
[Ietf.ALPN.DRAFT_15]: null,
|
|
324
|
+
};
|
|
325
|
+
// The default (`requireProtocol: false`) also advertises bare `qmux-01`,
|
|
326
|
+
// `qmux-00`, and `webtransport` so we still interop with relays that only
|
|
327
|
+
// know a wire-format version (today's moq-relay only accepts bare
|
|
328
|
+
// `webtransport`).
|
|
329
|
+
const quic = new Session(url, {
|
|
330
|
+
protocols: Object.keys(versions),
|
|
331
|
+
versions,
|
|
332
|
+
});
|
|
338
333
|
// Wait for the WebSocket to connect, or for the cancel promise to resolve.
|
|
339
334
|
// `ready` rejects on a refused/failed connection, so a throw here is the caller's
|
|
340
335
|
// cue to retry; a lost cancel race instead resolves and we close the loser.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/connection/connect.ts"],"names":[],"mappings":"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","sourcesContent":["import Session from \"@moq/qmux\";\nimport * as Ietf from \"../ietf/index.ts\";\nimport * as Lite from \"../lite/index.ts\";\nimport { Stream } from \"../stream.ts\";\nimport * as Hex from \"../util/hex.ts\";\nimport type { Established } from \"./established.ts\";\nimport { exchangeSetup } from \"./handshake.ts\";\n\n// Default head start for WebTransport before attempting the WebSocket fallback.\nconst DEFAULT_WEBSOCKET_DELAY_MS = 500;\n\n/** Tuning for the WebSocket fallback used when WebTransport is unavailable or loses the connect race. */\nexport interface WebSocketOptions {\n\t// If true (default), enable the WebSocket fallback.\n\tenabled?: boolean;\n\n\t// Optional: Use a different URL than WebTransport.\n\t// By default, `https` => `wss` and `http` => `ws`.\n\turl?: URL;\n\n\t// The delay in milliseconds before attempting the WebSocket fallback. (default: 500)\n\t// If WebSocket won the previous race for a given URL, this will be 0.\n\tdelay?: DOMHighResTimeStamp;\n}\n\n// One entry of `serverCertificateHashes`, used to pin a self-signed server.\n// Unlike the DOM type, `value` also accepts a hex string (the format moq\n// servers report via their certificate fingerprints), decoded automatically.\n/** A server certificate hash used to pin a self-signed server. `value` accepts raw bytes or a hex string. */\nexport interface CertificateHash {\n\talgorithm?: \"sha-256\";\n\tvalue: BufferSource | string;\n}\n\n// WebTransport options, extended with friendlier certificate pinning.\n/** WebTransport options extended with friendlier certificate pinning (hex hashes or a raw certificate). */\nexport interface WebTransportProps extends Omit<WebTransportOptions, \"serverCertificateHashes\"> {\n\t// Pin the server to one or more certificate hashes. Each `value` may be raw\n\t// bytes or a hex string; the algorithm defaults to `sha-256`.\n\tserverCertificateHashes?: CertificateHash[];\n\n\t// Pin the server by supplying its certificate directly; the SHA-256 hash is\n\t// computed for you. Accepts a PEM string or raw DER bytes. Use this when you\n\t// have the certificate but not its precomputed fingerprint.\n\tserverCertificate?: string | BufferSource;\n}\n\n/** Options for {@link connect}. */\nexport interface ConnectProps {\n\t// WebTransport options.\n\twebtransport?: WebTransportProps;\n\n\t// WebSocket (fallback) options.\n\twebsocket?: WebSocketOptions;\n\n\t// Use a pre-existing WebTransport session instead of connecting.\n\t// When provided, skips WebTransport/WebSocket race and uses this directly.\n\ttransport?: WebTransport;\n}\n\n// Save if WebSocket won the last race, so we won't give QUIC a head start next time.\nconst websocketWon = new Set<string>();\n\n// Firefox's WebTransport implementation drops server-initiated bidi streams,\n// breaking publish (the relay opens a subscribe bidi back to us). Force WebSocket.\n// TODO: remove once Firefox fixes incoming bidi delivery.\nconst isFirefox = typeof navigator !== \"undefined\" && navigator.userAgent.toLowerCase().includes(\"firefox\");\n\n/**\n * Establishes a connection to a MOQ server.\n *\n * @param url - The URL of the server to connect to\n * @returns A promise that resolves to a Connection instance\n */\nexport async function connect(url: URL, props?: ConnectProps): Promise<Established> {\n\tif (props?.transport) {\n\t\treturn connectTransport(url, props.transport);\n\t}\n\n\t// Create a cancel promise to kill whichever is still connecting.\n\tconst { promise: cancel, resolve: done } = Promise.withResolvers<void>();\n\n\tconst webtransport =\n\t\tglobalThis.WebTransport && !isFirefox ? connectWebTransport(url, cancel, props?.webtransport) : undefined;\n\n\t// Give QUIC a head start to connect before trying WebSocket, unless WebSocket has won in the past.\n\t// NOTE that QUIC should be faster because it involves 1/2 fewer RTTs.\n\tconst headstart =\n\t\t!webtransport || websocketWon.has(url.toString()) ? 0 : (props?.websocket?.delay ?? DEFAULT_WEBSOCKET_DELAY_MS);\n\tconst websocket =\n\t\tprops?.websocket?.enabled !== false\n\t\t\t? connectWebSocket(props?.websocket?.url ?? url, headstart, cancel)\n\t\t\t: undefined;\n\n\tif (!websocket && !webtransport) {\n\t\tthrow new Error(\"no transport available; WebTransport not supported and WebSocket is disabled\");\n\t}\n\n\t// Race the available transports, using `.any` to ignore if one participant has an error.\n\t// `webtransport`/`websocket` are `Promise | undefined`, so test existence explicitly: a\n\t// promise is always truthy, so bare truthiness here would be a misused-promise.\n\tconst session = await Promise.any(\n\t\twebtransport !== undefined\n\t\t\t? websocket !== undefined\n\t\t\t\t? [websocket, webtransport]\n\t\t\t\t: [webtransport]\n\t\t\t: [websocket],\n\t);\n\tdone();\n\n\tif (!session) throw new Error(\"no transport available\");\n\n\t// Save if WebSocket won the last race, so we won't give QUIC a head start next time.\n\tif (session instanceof Session) {\n\t\tconsole.warn(url.toString(), \"connected via WebSocket\");\n\t\twebsocketWon.add(url.toString());\n\t} else {\n\t\tconsole.log(url.toString(), \"connected via WebTransport\");\n\t}\n\n\t// Get the negotiated protocol. qmux Session exposes it directly;\n\t// native WebTransport doesn't have a standard .protocol property yet.\n\tconst protocol: string | undefined =\n\t\tsession instanceof Session\n\t\t\t? session.protocol || undefined\n\t\t\t: // @ts-expect-error - TODO: add protocol to WebTransport\n\t\t\t\tsession.protocol;\n\tconsole.debug(url.toString(), \"negotiated ALPN:\", protocol ?? \"(none)\");\n\n\t// Choose setup encoding based on negotiated WebTransport protocol (if any).\n\tlet setupVersion: Ietf.Version;\n\tconst modernVersion =\n\t\tprotocol === Ietf.ALPN.DRAFT_19\n\t\t\t? Ietf.Version.DRAFT_19\n\t\t\t: protocol === Ietf.ALPN.DRAFT_18\n\t\t\t\t? Ietf.Version.DRAFT_18\n\t\t\t\t: protocol === Ietf.ALPN.DRAFT_17\n\t\t\t\t\t? Ietf.Version.DRAFT_17\n\t\t\t\t\t: undefined;\n\tif (modernVersion !== undefined) {\n\t\treturn await handshakeAlpn(url, session as WebTransport, modernVersion);\n\t} else if (protocol === Ietf.ALPN.DRAFT_16) {\n\t\tsetupVersion = Ietf.Version.DRAFT_16;\n\t} else if (protocol === Ietf.ALPN.DRAFT_15) {\n\t\tsetupVersion = Ietf.Version.DRAFT_15;\n\t} else if (protocol === Lite.ALPN_05_WIP) {\n\t\t// moq-lite draft-05 exchanges SETUP on a dedicated unidirectional stream\n\t\t// (handled inside Connection), not the session compat stream.\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_05_WIP, undefined);\n\t} else if (protocol === Lite.ALPN_04) {\n\t\t// moq-lite draft-04 doesn't use a session stream, so we return immediately.\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_04, undefined);\n\t} else if (protocol === Lite.ALPN_03) {\n\t\t// moq-lite draft-03 doesn't use a session stream, so we return immediately.\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_03, undefined);\n\t} else if (protocol === Lite.ALPN || protocol === \"\" || protocol === undefined) {\n\t\t// moq-lite ALPN (or no protocol) uses Draft14 encoding for SETUP,\n\t\t// then negotiates the actual version via the SETUP message.\n\t\tsetupVersion = Ietf.Version.DRAFT_14;\n\t} else {\n\t\tthrow new Error(`unsupported WebTransport protocol: ${protocol}`);\n\t}\n\n\t// We're encoding 0x20 so it's backwards compatible with moq-transport-10+\n\tconst stream = await Stream.open(session);\n\tawait stream.writer.u53(Lite.StreamId.ClientCompat);\n\n\tconst encoder = new TextEncoder();\n\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n); // Allow a ton of request IDs.\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\")); // Put the implementation name in the parameters.\n\n\tconst client = new Ietf.ClientSetup({\n\t\t// NOTE: draft 15 onwards does not use CLIENT_SETUP to negotiate the version.\n\t\t// We still echo it just to make sure we're not accidentally trying to negotiate the version.\n\t\tversions:\n\t\t\tsetupVersion === Ietf.Version.DRAFT_16\n\t\t\t\t? [Ietf.Version.DRAFT_16]\n\t\t\t\t: setupVersion === Ietf.Version.DRAFT_15\n\t\t\t\t\t? [Ietf.Version.DRAFT_15]\n\t\t\t\t\t: [Lite.Version.DRAFT_02, Lite.Version.DRAFT_01, Ietf.Version.DRAFT_14],\n\t\tparameters: params,\n\t});\n\tconsole.debug(url.toString(), \"sending client setup\", client);\n\tawait client.encode(stream.writer, setupVersion);\n\n\t// And we expect 0x21 as the response.\n\tconst serverCompat = await stream.reader.u53();\n\tif (serverCompat !== Lite.StreamId.ServerCompat) {\n\t\tthrow new Error(`unsupported server message type: ${serverCompat.toString()}`);\n\t}\n\n\t// Decode ServerSetup in Draft14 format (version + params)\n\tconst server = await Ietf.ServerSetup.decode(stream.reader, setupVersion);\n\tconsole.debug(url.toString(), \"received server setup\", server);\n\n\tif (Object.values(Lite.Version).includes(server.version as Lite.Version)) {\n\t\treturn new Lite.Connection(url, session, server.version as Lite.Version, stream);\n\t} else if (Object.values(Ietf.Version).includes(server.version as Ietf.Version)) {\n\t\tconst maxRequestId = server.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;\n\t\treturn new Ietf.Connection({\n\t\t\turl,\n\t\t\tquic: session,\n\t\t\tcontrol: stream,\n\t\t\tmaxRequestId,\n\t\t\tversion: server.version as Ietf.IetfVersion,\n\t\t});\n\t} else {\n\t\tthrow new Error(`unsupported server version: ${server.version.toString()}`);\n\t}\n}\n\nasync function connectTransport(url: URL, session: WebTransport): Promise<Established> {\n\t// @ts-expect-error - TODO: add protocol to WebTransport\n\tconst protocol: string | undefined = session.protocol;\n\n\t// Choose setup encoding based on negotiated WebTransport protocol (if any).\n\tlet setupVersion: Ietf.Version;\n\tconst modernVersion =\n\t\tprotocol === Ietf.ALPN.DRAFT_19\n\t\t\t? Ietf.Version.DRAFT_19\n\t\t\t: protocol === Ietf.ALPN.DRAFT_18\n\t\t\t\t? Ietf.Version.DRAFT_18\n\t\t\t\t: protocol === Ietf.ALPN.DRAFT_17\n\t\t\t\t\t? Ietf.Version.DRAFT_17\n\t\t\t\t\t: undefined;\n\tif (modernVersion !== undefined) {\n\t\treturn await handshakeAlpn(url, session, modernVersion);\n\t} else if (protocol === Ietf.ALPN.DRAFT_16) {\n\t\tsetupVersion = Ietf.Version.DRAFT_16;\n\t} else if (protocol === Ietf.ALPN.DRAFT_15) {\n\t\tsetupVersion = Ietf.Version.DRAFT_15;\n\t} else if (protocol === Lite.ALPN_05_WIP) {\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_05_WIP, undefined);\n\t} else if (protocol === Lite.ALPN_04) {\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_04, undefined);\n\t} else if (protocol === Lite.ALPN_03) {\n\t\treturn new Lite.Connection(url, session, Lite.Version.DRAFT_03, undefined);\n\t} else if (protocol === Lite.ALPN || protocol === \"\" || protocol === undefined) {\n\t\tsetupVersion = Ietf.Version.DRAFT_14;\n\t} else {\n\t\tthrow new Error(`unsupported WebTransport protocol: ${protocol}`);\n\t}\n\n\tconst stream = await Stream.open(session);\n\tawait stream.writer.u53(Lite.StreamId.ClientCompat);\n\n\tconst encoder = new TextEncoder();\n\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\n\tconst client = new Ietf.ClientSetup({\n\t\tversions:\n\t\t\tsetupVersion === Ietf.Version.DRAFT_16\n\t\t\t\t? [Ietf.Version.DRAFT_16]\n\t\t\t\t: setupVersion === Ietf.Version.DRAFT_15\n\t\t\t\t\t? [Ietf.Version.DRAFT_15]\n\t\t\t\t\t: [Lite.Version.DRAFT_02, Lite.Version.DRAFT_01, Ietf.Version.DRAFT_14],\n\t\tparameters: params,\n\t});\n\tawait client.encode(stream.writer, setupVersion);\n\n\tconst serverCompat = await stream.reader.u53();\n\tif (serverCompat !== Lite.StreamId.ServerCompat) {\n\t\tthrow new Error(`unsupported server message type: ${serverCompat.toString()}`);\n\t}\n\n\tconst server = await Ietf.ServerSetup.decode(stream.reader, setupVersion);\n\n\tif (Object.values(Lite.Version).includes(server.version as Lite.Version)) {\n\t\treturn new Lite.Connection(url, session, server.version as Lite.Version, stream);\n\t} else if (Object.values(Ietf.Version).includes(server.version as Ietf.Version)) {\n\t\tconst maxRequestId = server.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;\n\t\treturn new Ietf.Connection({\n\t\t\turl,\n\t\t\tquic: session,\n\t\t\tcontrol: stream,\n\t\t\tmaxRequestId,\n\t\t\tversion: server.version as Ietf.IetfVersion,\n\t\t});\n\t} else {\n\t\tthrow new Error(`unsupported server version: ${server.version.toString()}`);\n\t}\n}\n\n/**\n * Draft-17+ client handshake. ALPN already pinned the version; SETUP is\n * exchanged over a pair of uni streams using stream type 0x2F00.\n */\nasync function handshakeAlpn(url: URL, session: WebTransport, version: Ietf.IetfVersion): Promise<Established> {\n\tconst controlStream = await exchangeSetup(session, version, \"moq-lite-js\");\n\n\treturn new Ietf.Connection({\n\t\turl,\n\t\tquic: session,\n\t\tcontrol: controlStream,\n\t\t// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.\n\t\tmaxRequestId: 0n,\n\t\tversion,\n\t});\n}\n\n// One entry of the DOM `serverCertificateHashes`, derived without naming the lib type.\ntype WebTransportHash = NonNullable<WebTransportOptions[\"serverCertificateHashes\"]>[number];\n\n// Strip PEM armor and base64-decode to the raw DER bytes.\nfunction pemToDer(pem: string): Uint8Array<ArrayBuffer> {\n\tconst match = pem.match(/-----BEGIN CERTIFICATE-----([\\s\\S]+?)-----END CERTIFICATE-----/);\n\tif (!match) {\n\t\tthrow new Error(\"invalid PEM certificate: missing -----BEGIN/END CERTIFICATE----- armor\");\n\t}\n\n\tconst binary = atob(match[1].replace(/\\s+/g, \"\"));\n\tconst der = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tder[i] = binary.charCodeAt(i);\n\t}\n\treturn der;\n}\n\n/**\n * Compute the SHA-256 hash of a certificate, the value `serverCertificateHashes`\n * pins. Accepts a PEM string or raw DER bytes. Matches the hex fingerprints a moq\n * server reports, so `Hex.fromBytes(await certificateHash(pem))` round-trips.\n */\nexport async function certificateHash(cert: string | BufferSource): Promise<Uint8Array<ArrayBuffer>> {\n\tconst der = typeof cert === \"string\" ? pemToDer(cert) : cert;\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", der);\n\treturn new Uint8Array(digest);\n}\n\n// Normalize our friendlier pinning options into the DOM `serverCertificateHashes`.\nasync function resolveCertificateHashes(options?: WebTransportProps): Promise<WebTransportHash[] | undefined> {\n\tconst hashes: WebTransportHash[] = [];\n\n\tfor (const hash of options?.serverCertificateHashes ?? []) {\n\t\tconst value = typeof hash.value === \"string\" ? Hex.toBytes(hash.value) : hash.value;\n\t\thashes.push({ algorithm: hash.algorithm ?? \"sha-256\", value });\n\t}\n\n\tif (options?.serverCertificate !== undefined) {\n\t\thashes.push({ algorithm: \"sha-256\", value: await certificateHash(options.serverCertificate) });\n\t}\n\n\treturn hashes.length > 0 ? hashes : undefined;\n}\n\nasync function connectWebTransport(\n\turl: URL,\n\tcancel: Promise<void>,\n\toptions?: WebTransportProps,\n): Promise<WebTransport | undefined> {\n\tlet finalUrl = url;\n\n\t// Our custom pinning fields are normalized separately; the rest are DOM options.\n\tconst { serverCertificate: _cert, serverCertificateHashes: _hashes, ...webtransport } = options ?? {};\n\n\tconst finalOptions: WebTransportOptions = {\n\t\tallowPooling: false,\n\t\tcongestionControl: \"low-latency\",\n\t\tprotocols: [\n\t\t\tLite.ALPN_04,\n\t\t\tLite.ALPN_03,\n\t\t\tLite.ALPN,\n\t\t\t// NOTE(norsk): the IETF draft ALPNs (moqt-15..19) are intentionally NOT\n\t\t\t// offered. norsk advertises moqt-16, and net's draft-16 data-plane\n\t\t\t// reader can't yet consume norsk's (correct) draft-16 subgroup objects,\n\t\t\t// so negotiating moqt-16 breaks media playback. Offering only the\n\t\t\t// moq-lite ALPNs makes the WebTransport handshake settle on no IETF\n\t\t\t// protocol → net's draft-14 fallback (connect.ts §\"Lite.ALPN | ''\"),\n\t\t\t// which norsk serves fine. Restore these once net draft-16 works.\n\t\t],\n\t\t...webtransport,\n\t};\n\n\t// Accumulate caller-provided pins first, then append anything we fetch below,\n\t// so a fetched fingerprint never clobbers hashes passed in via options.\n\tconst hashes = (await resolveCertificateHashes(options)) ?? [];\n\n\t// Only perform certificate fetch and URL rewrite when polyfill is not needed\n\t// This is needed because WebTransport is a butt to work with in local development.\n\tif (url.protocol === \"http:\") {\n\t\tconst fingerprintUrl = new URL(url);\n\t\tfingerprintUrl.pathname = \"/certificate.sha256\";\n\t\tfingerprintUrl.search = \"\";\n\t\t// Dev-only path: http:// can't be a real WebTransport origin, so we fetch the\n\t\t// self-signed cert's hash over plain HTTP and pin it. Production uses https://\n\t\t// and never reaches here. Keep this at debug so it doesn't read as a problem.\n\t\tconsole.debug(\n\t\t\tfingerprintUrl.toString(),\n\t\t\t\"performing an insecure fingerprint fetch; use https:// in production\",\n\t\t);\n\n\t\t// Fetch the fingerprint from the server.\n\t\t// TODO cancel the request if the effect is cancelled.\n\t\tconst fingerprint = await Promise.race([fetch(fingerprintUrl), cancel]);\n\t\tif (!fingerprint) return undefined;\n\n\t\tconst fingerprintText = await Promise.race([fingerprint.text(), cancel]);\n\t\tif (fingerprintText === undefined) return undefined;\n\n\t\thashes.push({ algorithm: \"sha-256\", value: Hex.toBytes(fingerprintText) });\n\n\t\tfinalUrl = new URL(url);\n\t\tfinalUrl.protocol = \"https:\";\n\t}\n\n\tif (hashes.length > 0) {\n\t\tfinalOptions.serverCertificateHashes = hashes;\n\t}\n\n\tconst quic = new WebTransport(finalUrl, finalOptions);\n\n\t// Both .ready and .closed reject on failure; catch .closed to avoid an unhandled rejection.\n\tquic.closed.catch(() => {});\n\n\t// Wait for the WebTransport to connect, or for the cancel promise to resolve.\n\t// Close the connection if we lost the race.\n\tconst loaded = await Promise.race([quic.ready.then(() => true), cancel]);\n\tif (!loaded) {\n\t\tquic.close();\n\t\treturn undefined;\n\t}\n\n\treturn quic;\n}\n\n// TODO accept arguments to control the port/path used.\nasync function connectWebSocket(url: URL, delay: number, cancel: Promise<void>): Promise<Session | undefined> {\n\tconst timer = new Promise<void>((resolve) => setTimeout(resolve, delay));\n\n\tconst active = await Promise.race([cancel, timer.then(() => true)]);\n\tif (!active) return undefined;\n\n\tconst quic = new Session(url);\n\n\t// Wait for the WebSocket to connect, or for the cancel promise to resolve.\n\t// `ready` rejects on a refused/failed connection, so a throw here is the caller's\n\t// cue to retry; a lost cancel race instead resolves and we close the loser.\n\tconst loaded = await Promise.race([quic.ready.then(() => true), cancel]);\n\tif (!loaded) {\n\t\tquic.close();\n\t\treturn undefined;\n\t}\n\n\treturn quic;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/connection/connect.ts"],"names":[],"mappings":"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","sourcesContent":["import Session, { type Version as QmuxVersion } from \"@moq/qmux\";\nimport * as Ietf from \"../ietf/index.ts\";\nimport * as Lite from \"../lite/index.ts\";\nimport { Stream } from \"../stream.ts\";\nimport * as Hex from \"../util/hex.ts\";\nimport { isWebTransportSupported } from \"./browser.ts\";\nimport type { Established } from \"./established.ts\";\nimport { exchangeSetup } from \"./handshake.ts\";\n\n// Default head start for WebTransport before attempting the WebSocket fallback.\nconst DEFAULT_WEBSOCKET_DELAY_MS = 500;\n\n/** Tuning for the WebSocket fallback used when WebTransport is unavailable or loses the connect race. */\nexport interface WebSocketOptions {\n\t/** Enable the WebSocket fallback. Defaults to `true`. */\n\tenabled?: boolean;\n\n\t/** Use a different URL than WebTransport. By default, `https` maps to `wss` and `http` to `ws`. */\n\turl?: URL;\n\n\t/**\n\t * The delay in milliseconds before attempting the WebSocket fallback (default: 500).\n\t * If WebSocket won the previous race for a given URL, this is 0.\n\t */\n\tdelay?: DOMHighResTimeStamp;\n}\n\n/**\n * A server certificate hash used to pin a self-signed server, one entry of\n * `serverCertificateHashes`. Unlike the DOM type, `value` also accepts a hex string (the\n * format moq servers report via their certificate fingerprints), decoded automatically.\n */\nexport interface CertificateHash {\n\t/** The hash algorithm. Defaults to `sha-256` (the only supported value). */\n\talgorithm?: \"sha-256\";\n\t/** The certificate hash: raw bytes or a hex string. */\n\tvalue: BufferSource | string;\n}\n\n/** WebTransport options extended with friendlier certificate pinning (hex hashes or a raw certificate). */\nexport interface WebTransportProps extends Omit<WebTransportOptions, \"serverCertificateHashes\"> {\n\t/**\n\t * Pin the server to one or more certificate hashes. Each `value` may be raw\n\t * bytes or a hex string; the algorithm defaults to `sha-256`.\n\t */\n\tserverCertificateHashes?: CertificateHash[];\n\n\t/**\n\t * Pin the server by supplying its certificate directly; the SHA-256 hash is\n\t * computed for you. Accepts a PEM string or raw DER bytes. Use this when you\n\t * have the certificate but not its precomputed fingerprint.\n\t */\n\tserverCertificate?: string | BufferSource;\n}\n\n/** Options for {@link connect}. */\nexport interface ConnectProps {\n\t/** WebTransport options. */\n\twebtransport?: WebTransportProps;\n\n\t/** WebSocket (fallback) options. */\n\twebsocket?: WebSocketOptions;\n\n\t/**\n\t * Use a pre-existing WebTransport session instead of connecting; skips the\n\t * WebTransport/WebSocket race. The publisher acquires the session's datagram\n\t * writer lock (`datagrams.writable.getWriter()`) for the session's lifetime,\n\t * so the caller must not hold it. Aborting via {@link ConnectProps.signal}\n\t * closes this session, so don't share one you intend to reuse afterwards.\n\t */\n\ttransport?: WebTransport;\n\n\t/**\n\t * Whether the relay supports broadcast discovery; see {@link Established.discovery}.\n\t * Defaults to true, except for relays known to lack it.\n\t */\n\tdiscovery?: boolean;\n\n\t/**\n\t * Aborts the connection attempt with the signal's reason. An already-aborted\n\t * signal rejects before anything opens, and aborting after the connection is\n\t * established has no effect. Use `AbortSignal.timeout(ms)` for a deadline.\n\t */\n\tsignal?: AbortSignal;\n}\n\n// Relays that don't implement broadcast discovery (SUBSCRIBE_NAMESPACE), so `announced()` would\n// never yield and a consumer waiting on an announcement would hang forever. Override with the\n// `discovery` option. Drop a host once its relay ships discovery.\nconst NO_DISCOVERY_HOSTS = [\"mediaoverquic.com\"];\n\n/** Whether the relay at `url` is expected to support broadcast discovery. */\nfunction defaultDiscovery(url: URL): boolean {\n\treturn !NO_DISCOVERY_HOSTS.some((host) => url.hostname.endsWith(host));\n}\n\n// Save if WebSocket won the last race, so we won't give QUIC a head start next time.\nconst websocketWon = new Set<string>();\n\n/** The default connect signal: never aborts. */\nconst NEVER_ABORTED = new AbortController().signal;\n\n/**\n * Establishes a connection to a MOQ server.\n *\n * @param url - The URL of the server to connect to\n * @param props - Connection options\n * @returns A promise that resolves to a Connection instance\n */\nexport async function connect(url: URL, props?: ConnectProps): Promise<Established> {\n\tconst signal = props?.signal ?? NEVER_ABORTED;\n\tsignal.throwIfAborted();\n\n\t// Resolves on abort so every in-flight transport tears itself down.\n\tconst { promise: abort, resolve } = Promise.withResolvers<void>();\n\tconst onAbort = () => resolve();\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\n\tconst pending = connectInner(url, props, abort);\n\ttry {\n\t\t// A `pending` rejection propagates unless the abort beat it to the finish line.\n\t\tconst connection = await Promise.race([pending, abort.then(() => undefined)]);\n\t\tif (connection && !signal.aborted) return connection;\n\n\t\t// Close a connection that settles after the abort.\n\t\tpending.then((conn) => conn.close()).catch(() => {});\n\t\tthrow signal.reason;\n\t} finally {\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nasync function connectInner(url: URL, props: ConnectProps | undefined, abort: Promise<void>): Promise<Established> {\n\tconst discovery = props?.discovery ?? defaultDiscovery(url);\n\n\tif (props?.transport) {\n\t\tconst transport = props.transport;\n\t\tvoid abort.then(() => transport.close());\n\t\treturn connectTransport(url, transport, discovery);\n\t}\n\n\t// Stop transports after one connects or the caller aborts.\n\tconst { promise: raced, resolve: done } = Promise.withResolvers<void>();\n\tconst cancel = Promise.race([raced, abort]);\n\n\tconst webtransport = isWebTransportSupported() ? connectWebTransport(url, cancel, props?.webtransport) : undefined;\n\n\t// Give QUIC a head start to connect before trying WebSocket, unless WebSocket has won in the past.\n\t// NOTE that QUIC should be faster because it involves 1/2 fewer RTTs.\n\tconst headstart =\n\t\t!webtransport || websocketWon.has(url.toString()) ? 0 : (props?.websocket?.delay ?? DEFAULT_WEBSOCKET_DELAY_MS);\n\tconst websocket =\n\t\tprops?.websocket?.enabled !== false\n\t\t\t? connectWebSocket(props?.websocket?.url ?? url, headstart, cancel)\n\t\t\t: undefined;\n\n\tif (!websocket && !webtransport) {\n\t\tthrow new Error(\"no transport available; WebTransport not supported and WebSocket is disabled\");\n\t}\n\n\t// Race the available transports, using `.any` to ignore if one participant has an error.\n\t// `webtransport`/`websocket` are `Promise | undefined`, so test existence explicitly: a\n\t// promise is always truthy, so bare truthiness here would be a misused-promise.\n\tconst session = await Promise.any(\n\t\twebtransport !== undefined\n\t\t\t? websocket !== undefined\n\t\t\t\t? [websocket, webtransport]\n\t\t\t\t: [webtransport]\n\t\t\t: [websocket],\n\t);\n\tdone();\n\n\tif (!session) throw new Error(\"no transport available\");\n\n\t// Abort the setup handshake without leaking the selected transport.\n\tvoid abort.then(() => session.close());\n\n\t// Save if WebSocket won the last race, so we won't give QUIC a head start next time.\n\tif (session instanceof Session) {\n\t\tconsole.warn(url.toString(), \"connected via WebSocket\");\n\t\twebsocketWon.add(url.toString());\n\t} else {\n\t\tconsole.debug(url.toString(), \"connected via WebTransport\");\n\t}\n\n\t// The remaining setup is identical whether the transport was raced or supplied.\n\treturn await connectTransport(url, session as WebTransport, discovery);\n}\n\nasync function connectTransport(url: URL, session: WebTransport, discovery: boolean): Promise<Established> {\n\t// qmux Session exposes the negotiated protocol directly (as \"\" when there is none);\n\t// native WebTransport doesn't have a standard .protocol property yet.\n\tconst protocol: string | undefined = (session as { protocol?: string }).protocol || undefined;\n\tconsole.debug(url.toString(), \"negotiated ALPN:\", protocol ?? \"(none)\");\n\n\t// Choose setup encoding based on negotiated WebTransport protocol (if any).\n\tlet setupVersion: Ietf.Version;\n\tconst modernVersion =\n\t\tprotocol === Ietf.ALPN.DRAFT_20\n\t\t\t? Ietf.Version.DRAFT_20\n\t\t\t: protocol === Ietf.ALPN.DRAFT_19\n\t\t\t\t? Ietf.Version.DRAFT_19\n\t\t\t\t: protocol === Ietf.ALPN.DRAFT_18\n\t\t\t\t\t? Ietf.Version.DRAFT_18\n\t\t\t\t\t: protocol === Ietf.ALPN.DRAFT_17\n\t\t\t\t\t\t? Ietf.Version.DRAFT_17\n\t\t\t\t\t\t: undefined;\n\tif (modernVersion !== undefined) {\n\t\treturn await handshakeAlpn(url, session, modernVersion, discovery);\n\t} else if (protocol === Ietf.ALPN.DRAFT_16) {\n\t\tsetupVersion = Ietf.Version.DRAFT_16;\n\t} else if (protocol === Ietf.ALPN.DRAFT_15) {\n\t\tsetupVersion = Ietf.Version.DRAFT_15;\n\t} else if (protocol === Lite.ALPN_06_WIP) {\n\t\treturn new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_06, discovery });\n\t} else if (protocol === Lite.ALPN_05) {\n\t\treturn new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_05, discovery });\n\t} else if (protocol === Lite.ALPN_04) {\n\t\treturn new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_04, discovery });\n\t} else if (protocol === Lite.ALPN_03) {\n\t\treturn new Lite.Connection({ url, quic: session, version: Lite.Version.DRAFT_03, discovery });\n\t} else if (protocol === Lite.ALPN || protocol === \"\" || protocol === undefined) {\n\t\tsetupVersion = Ietf.Version.DRAFT_14;\n\t} else {\n\t\tthrow new Error(`unsupported WebTransport protocol: ${protocol}`);\n\t}\n\n\tconst stream = await Stream.open(session);\n\tawait stream.writer.u53(Lite.StreamId.ClientCompat);\n\n\tconst encoder = new TextEncoder();\n\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\tIetf.solicitIntoSetup(params);\n\n\tconst client = new Ietf.ClientSetup({\n\t\tversions:\n\t\t\tsetupVersion === Ietf.Version.DRAFT_16\n\t\t\t\t? [Ietf.Version.DRAFT_16]\n\t\t\t\t: setupVersion === Ietf.Version.DRAFT_15\n\t\t\t\t\t? [Ietf.Version.DRAFT_15]\n\t\t\t\t\t: [Lite.Version.DRAFT_02, Lite.Version.DRAFT_01, Ietf.Version.DRAFT_14],\n\t\tparameters: params,\n\t});\n\tawait client.encode(stream.writer, setupVersion);\n\n\tconst serverCompat = await stream.reader.u53();\n\tif (serverCompat !== Lite.StreamId.ServerCompat) {\n\t\tthrow new Error(`unsupported server message type: ${serverCompat.toString()}`);\n\t}\n\n\tconst server = await Ietf.ServerSetup.decode(stream.reader, setupVersion);\n\n\tif (Object.values(Lite.Version).includes(server.version as Lite.Version)) {\n\t\treturn new Lite.Connection({\n\t\t\turl,\n\t\t\tquic: session,\n\t\t\tversion: server.version as Lite.Version,\n\t\t\tsession: stream,\n\t\t\tdiscovery,\n\t\t});\n\t} else if (Object.values(Ietf.Version).includes(server.version as Ietf.Version)) {\n\t\tconst maxRequestId = server.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;\n\t\treturn new Ietf.Connection({\n\t\t\tdiscovery,\n\t\t\tclient: true,\n\t\t\turl,\n\t\t\tquic: session,\n\t\t\tcontrol: stream,\n\t\t\tmaxRequestId,\n\t\t\tversion: server.version as Ietf.IetfVersion,\n\t\t\tsolicit: Ietf.solicitFromSetup(server.parameters),\n\t\t});\n\t} else {\n\t\tthrow new Error(`unsupported server version: ${server.version.toString()}`);\n\t}\n}\n\n/**\n * Draft-17+ client handshake. ALPN already pinned the version; SETUP is\n * exchanged over a pair of uni streams using stream type 0x2F00.\n */\nasync function handshakeAlpn(\n\turl: URL,\n\tsession: WebTransport,\n\tversion: Ietf.IetfVersion,\n\tdiscovery: boolean,\n): Promise<Established> {\n\tconst { control, solicit, cluster } = await exchangeSetup(session, version, \"moq-lite-js\");\n\n\treturn new Ietf.Connection({\n\t\tdiscovery,\n\t\tclient: true,\n\t\turl,\n\t\tquic: session,\n\t\tcontrol,\n\t\tsolicit,\n\t\tcluster,\n\t\t// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.\n\t\tmaxRequestId: 0n,\n\t\tversion,\n\t});\n}\n\n// One entry of the DOM `serverCertificateHashes`, derived without naming the lib type.\ntype WebTransportHash = NonNullable<WebTransportOptions[\"serverCertificateHashes\"]>[number];\n\n// Strip PEM armor and base64-decode to the raw DER bytes.\nfunction pemToDer(pem: string): Uint8Array<ArrayBuffer> {\n\tconst match = pem.match(/-----BEGIN CERTIFICATE-----([\\s\\S]+?)-----END CERTIFICATE-----/);\n\tif (!match) {\n\t\tthrow new Error(\"invalid PEM certificate: missing -----BEGIN/END CERTIFICATE----- armor\");\n\t}\n\n\tconst binary = atob(match[1].replace(/\\s+/g, \"\"));\n\tconst der = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tder[i] = binary.charCodeAt(i);\n\t}\n\treturn der;\n}\n\n/**\n * Compute the SHA-256 hash of a certificate, the value `serverCertificateHashes`\n * pins. Accepts a PEM string or raw DER bytes. Matches the hex fingerprints a moq\n * server reports, so `Hex.fromBytes(await certificateHash(pem))` round-trips.\n */\nexport async function certificateHash(cert: string | BufferSource): Promise<Uint8Array<ArrayBuffer>> {\n\tconst der = typeof cert === \"string\" ? pemToDer(cert) : cert;\n\tconst digest = await crypto.subtle.digest(\"SHA-256\", der);\n\treturn new Uint8Array(digest);\n}\n\n// Normalize our friendlier pinning options into the DOM `serverCertificateHashes`.\nasync function resolveCertificateHashes(options?: WebTransportProps): Promise<WebTransportHash[] | undefined> {\n\tconst hashes: WebTransportHash[] = [];\n\n\tfor (const hash of options?.serverCertificateHashes ?? []) {\n\t\tconst value = typeof hash.value === \"string\" ? Hex.toBytes(hash.value) : hash.value;\n\t\thashes.push({ algorithm: hash.algorithm ?? \"sha-256\", value });\n\t}\n\n\tif (options?.serverCertificate !== undefined) {\n\t\thashes.push({ algorithm: \"sha-256\", value: await certificateHash(options.serverCertificate) });\n\t}\n\n\treturn hashes.length > 0 ? hashes : undefined;\n}\n\nasync function connectWebTransport(\n\turl: URL,\n\tcancel: Promise<void>,\n\toptions?: WebTransportProps,\n): Promise<WebTransport | undefined> {\n\tlet finalUrl = url;\n\n\t// Our custom pinning fields are normalized separately; the rest are DOM options.\n\tconst { serverCertificate: _cert, serverCertificateHashes: _hashes, ...webtransport } = options ?? {};\n\n\tconst finalOptions: WebTransportOptions = {\n\t\tallowPooling: false,\n\t\tcongestionControl: \"low-latency\",\n\t\tprotocols: [\n\t\t\t// Lite.ALPN_06_WIP is intentionally omitted: lite-06 is work-in-progress and\n\t\t\t// not advertised by default (connect.ts still accepts it if a server negotiates it).\n\t\t\tLite.ALPN_05,\n\t\t\tLite.ALPN_04,\n\t\t\tLite.ALPN_03,\n\t\t\tLite.ALPN,\n\t\t\tIetf.ALPN.DRAFT_20,\n\t\t\tIetf.ALPN.DRAFT_19,\n\t\t\tIetf.ALPN.DRAFT_18,\n\t\t\tIetf.ALPN.DRAFT_17,\n\t\t\tIetf.ALPN.DRAFT_16,\n\t\t\tIetf.ALPN.DRAFT_15,\n\t\t],\n\t\t...webtransport,\n\t};\n\n\t// Accumulate caller-provided pins first, then append anything we fetch below,\n\t// so a fetched fingerprint never clobbers hashes passed in via options.\n\tconst hashes = (await resolveCertificateHashes(options)) ?? [];\n\n\t// Only perform certificate fetch and URL rewrite when polyfill is not needed\n\t// This is needed because WebTransport is a butt to work with in local development.\n\tif (url.protocol === \"http:\") {\n\t\tconst fingerprintUrl = new URL(url);\n\t\tfingerprintUrl.pathname = \"/certificate.sha256\";\n\t\tfingerprintUrl.search = \"\";\n\t\t// Dev-only path: http:// can't be a real WebTransport origin, so we fetch the\n\t\t// self-signed cert's hash over plain HTTP and pin it. Production uses https://\n\t\t// and never reaches here. Keep this at debug so it doesn't read as a problem.\n\t\tconsole.debug(\n\t\t\tfingerprintUrl.toString(),\n\t\t\t\"performing an insecure fingerprint fetch; use https:// in production\",\n\t\t);\n\n\t\t// Fetch the fingerprint from the server.\n\t\t// TODO cancel the request if the effect is cancelled.\n\t\tconst fingerprint = await Promise.race([fetch(fingerprintUrl), cancel]);\n\t\tif (!fingerprint) return undefined;\n\n\t\tconst fingerprintText = await Promise.race([fingerprint.text(), cancel]);\n\t\tif (fingerprintText === undefined) return undefined;\n\n\t\thashes.push({ algorithm: \"sha-256\", value: Hex.toBytes(fingerprintText) });\n\n\t\tfinalUrl = new URL(url);\n\t\tfinalUrl.protocol = \"https:\";\n\t}\n\n\tif (hashes.length > 0) {\n\t\tfinalOptions.serverCertificateHashes = hashes;\n\t}\n\n\tconst quic = new WebTransport(finalUrl, finalOptions);\n\n\t// Both .ready and .closed reject on failure; catch .closed to avoid an unhandled rejection.\n\tquic.closed.catch(() => {});\n\n\t// Wait for the WebTransport to connect, or for the cancel promise to resolve.\n\t// Close the connection if we lost the race.\n\tconst loaded = await Promise.race([quic.ready.then(() => true), cancel]);\n\tif (!loaded) {\n\t\tquic.close();\n\t\treturn undefined;\n\t}\n\n\treturn quic;\n}\n\n// TODO accept arguments to control the port/path used.\nasync function connectWebSocket(url: URL, delay: number, cancel: Promise<void>): Promise<Session | undefined> {\n\tconst timer = new Promise<void>((resolve) => setTimeout(resolve, delay));\n\n\tconst active = await Promise.race([cancel, timer.then(() => true)]);\n\tif (!active) return undefined;\n\n\t// Only moq-transport-18 is pinned to qmux-01 today. Every other ALPN we\n\t// support is currently negotiated as `qmux-00.{alpn}` on the wire, but we\n\t// don't want to lock that in: set the value to `null` so the polyfill\n\t// advertises every QMux draft it knows about and the server picks one.\n\t// Insertion order is the negotiation preference on the wire.\n\tconst versions = {\n\t\t// Lite.ALPN_06_WIP omitted on purpose: lite-06 is work-in-progress, not advertised by default.\n\t\t[Lite.ALPN_05]: null,\n\t\t[Lite.ALPN_04]: null,\n\t\t[Lite.ALPN_03]: null,\n\t\t[Lite.ALPN]: null,\n\t\t[Ietf.ALPN.DRAFT_20]: \"qmux-01\",\n\t\t[Ietf.ALPN.DRAFT_18]: \"qmux-01\",\n\t\t[Ietf.ALPN.DRAFT_17]: null,\n\t\t[Ietf.ALPN.DRAFT_16]: null,\n\t\t[Ietf.ALPN.DRAFT_15]: null,\n\t} as const satisfies Record<string, QmuxVersion | QmuxVersion[] | null>;\n\n\t// The default (`requireProtocol: false`) also advertises bare `qmux-01`,\n\t// `qmux-00`, and `webtransport` so we still interop with relays that only\n\t// know a wire-format version (today's moq-relay only accepts bare\n\t// `webtransport`).\n\tconst quic = new Session(url, {\n\t\tprotocols: Object.keys(versions),\n\t\tversions,\n\t});\n\n\t// Wait for the WebSocket to connect, or for the cancel promise to resolve.\n\t// `ready` rejects on a refused/failed connection, so a throw here is the caller's\n\t// cue to retry; a lost cancel race instead resolves and we close the loser.\n\tconst loaded = await Promise.race([quic.ready.then(() => true), cancel]);\n\tif (!loaded) {\n\t\tquic.close();\n\t\treturn undefined;\n\t}\n\n\treturn quic;\n}\n"]}
|