oysterun 1.5.7-beta.7 → 1.5.7-beta.8
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/dev/client/connectivity/internal-connectivity-status.d.mts +14 -0
- package/dev/client/connectivity/internal-connectivity-status.mjs +59 -0
- package/dev/client/connectivity/logical-host-connection.mjs +21 -14
- package/dev/client/connectivity/managed-host-browser.d.mts +20 -0
- package/dev/client/connectivity/managed-host-browser.mjs +128 -34
- package/dev/client/connectivity/webrtc-logical-transport.mjs +336 -22
- package/dev/client/web/index.html +487 -63
- package/dev/client/web-chat/dist/assets/{ReactPrism-DZ1Ev_vc.js → ReactPrism-BVwTeJ2J.js} +1 -1
- package/dev/client/web-chat/dist/assets/{ReactPrism-DZ1Ev_vc.js.map → ReactPrism-BVwTeJ2J.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/{index-D-3SLM-g.js → index-B0-fm02U.js} +80 -80
- package/dev/client/web-chat/dist/assets/index-B0-fm02U.js.map +1 -0
- package/dev/client/web-chat/dist/assets/{index-BxiONAdX.js → index-BIJqUapL.js} +2 -2
- package/dev/client/web-chat/dist/assets/{index-BxiONAdX.js.map → index-BIJqUapL.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/{index-f13huqVE.js → index-Bcsmmpmp.js} +1 -1
- package/dev/client/web-chat/dist/assets/{index-f13huqVE.js.map → index-Bcsmmpmp.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/{pdf-DqKSOCNK.js → pdf-Dx9dlfRj.js} +3 -3
- package/dev/client/web-chat/dist/assets/pdf-Dx9dlfRj.js.map +1 -0
- package/dev/client/web-chat/dist/assets/{web-LmiNr0Ov.js → web-CNSm9WCL.js} +1 -1
- package/dev/client/web-chat/dist/assets/{web-LmiNr0Ov.js.map → web-CNSm9WCL.js.map} +1 -1
- package/dev/client/web-chat/dist/assets/{web-BgssiYmu.js → web-Ql5RPS3a.js} +1 -1
- package/dev/client/web-chat/dist/assets/{web-BgssiYmu.js.map → web-Ql5RPS3a.js.map} +1 -1
- package/dev/client/web-chat/dist/index.html +1 -1
- package/host-service/config.mjs +7 -0
- package/host-service/connectivity/webrtc-host-dispatcher.mjs +18 -10
- package/host-service/connectivity/webrtc-signaling-service.mjs +67 -16
- package/host-service/matrix-facade.mjs +16 -0
- package/host-service/package.json +1 -1
- package/package.json +1 -1
- package/dev/client/web-chat/dist/assets/index-D-3SLM-g.js.map +0 -1
- package/dev/client/web-chat/dist/assets/pdf-DqKSOCNK.js.map +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type InternalConnectivityStatus = {
|
|
2
|
+
matrix: "Connected" | "Reconnecting" | "Error" | "Offline";
|
|
3
|
+
activePath: "WebRTC" | "Bootstrap" | "Direct" | "None";
|
|
4
|
+
promotion: "Idle" | `Attempt ${number}/4` | "Quiesced" | "Not applicable";
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const INTERNAL_CONNECTIVITY_PROMOTION_ATTEMPT_LIMIT: 4;
|
|
8
|
+
export function deriveInternalConnectivityStatus(input?: {
|
|
9
|
+
syncState?: string | null;
|
|
10
|
+
transportStatus?: Record<string, unknown>;
|
|
11
|
+
}): InternalConnectivityStatus;
|
|
12
|
+
export function internalConnectivityStatusText(
|
|
13
|
+
status: InternalConnectivityStatus,
|
|
14
|
+
): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const MATRIX_STATUS = Object.freeze({
|
|
2
|
+
ERROR: "Error",
|
|
3
|
+
CATCHUP: "Reconnecting",
|
|
4
|
+
RECONNECTING: "Reconnecting",
|
|
5
|
+
PREPARED: "Connected",
|
|
6
|
+
SYNCING: "Connected",
|
|
7
|
+
STOPPED: "Offline",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const ACTIVE_PATH_STATUS = Object.freeze({
|
|
11
|
+
webrtc: "WebRTC",
|
|
12
|
+
bootstrap: "Bootstrap",
|
|
13
|
+
direct_host: "Direct",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const INTERNAL_CONNECTIVITY_PROMOTION_ATTEMPT_LIMIT = 4;
|
|
17
|
+
|
|
18
|
+
function finiteNonNegativeInteger(value) {
|
|
19
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function deriveInternalConnectivityStatus({
|
|
23
|
+
syncState = null,
|
|
24
|
+
transportStatus = {},
|
|
25
|
+
} = {}) {
|
|
26
|
+
const browserOffline = transportStatus.browser_online === false;
|
|
27
|
+
const transportConnectionState = transportStatus.connection_state;
|
|
28
|
+
const matrix = browserOffline
|
|
29
|
+
? "Offline"
|
|
30
|
+
: MATRIX_STATUS[syncState] ||
|
|
31
|
+
(transportConnectionState === "recovering" ||
|
|
32
|
+
transportConnectionState === "disconnected"
|
|
33
|
+
? "Reconnecting"
|
|
34
|
+
: transportConnectionState === "connected"
|
|
35
|
+
? "Connected"
|
|
36
|
+
: "Offline");
|
|
37
|
+
const activePath =
|
|
38
|
+
ACTIVE_PATH_STATUS[transportStatus.active_path_kind] || "None";
|
|
39
|
+
|
|
40
|
+
let promotion = "Not applicable";
|
|
41
|
+
if (transportStatus.managed_transport_enabled === true) {
|
|
42
|
+
const attempt = Math.min(
|
|
43
|
+
finiteNonNegativeInteger(transportStatus.promotion_attempt_count),
|
|
44
|
+
INTERNAL_CONNECTIVITY_PROMOTION_ATTEMPT_LIMIT,
|
|
45
|
+
);
|
|
46
|
+
promotion =
|
|
47
|
+
transportStatus.retry_quiesced === true
|
|
48
|
+
? "Quiesced"
|
|
49
|
+
: attempt > 0
|
|
50
|
+
? `Attempt ${attempt}/${INTERNAL_CONNECTIVITY_PROMOTION_ATTEMPT_LIMIT}`
|
|
51
|
+
: "Idle";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Object.freeze({ matrix, activePath, promotion });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function internalConnectivityStatusText(status) {
|
|
58
|
+
return `Debug Network Connection Status · Matrix: ${status.matrix} · Active path: ${status.activePath} · WebRTC promotion: ${status.promotion}`;
|
|
59
|
+
}
|
|
@@ -16,7 +16,9 @@ async function withTimeout(promise, timeoutMs, message) {
|
|
|
16
16
|
const timeout = new Promise((_, reject) => {
|
|
17
17
|
timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
18
18
|
});
|
|
19
|
-
return Promise.race([promise, timeout]).finally(() =>
|
|
19
|
+
return Promise.race([promise, timeout]).finally(() =>
|
|
20
|
+
clearTimeout(timeoutId)
|
|
21
|
+
);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
function headersToObject(headers) {
|
|
@@ -84,13 +86,22 @@ async function fetchWithDeadline(fetchImpl, url, options, timeoutMs) {
|
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
export class BootstrapFetchTransport {
|
|
87
|
-
constructor({
|
|
89
|
+
constructor({
|
|
90
|
+
fetchImpl = globalThis.fetch,
|
|
91
|
+
origin = "",
|
|
92
|
+
diagnostic,
|
|
93
|
+
responseGuard = null,
|
|
94
|
+
} = {}) {
|
|
88
95
|
if (typeof fetchImpl !== "function") {
|
|
89
96
|
throw new Error("Bootstrap fetch implementation is required");
|
|
90
97
|
}
|
|
98
|
+
if (responseGuard !== null && typeof responseGuard !== "function") {
|
|
99
|
+
throw new Error("Bootstrap response guard must be a function");
|
|
100
|
+
}
|
|
91
101
|
this.fetchImpl = fetchImpl;
|
|
92
102
|
this.origin = origin.replace(/\/+$/, "");
|
|
93
103
|
this.diagnostic = diagnostic;
|
|
104
|
+
this.responseGuard = responseGuard;
|
|
94
105
|
this.pathKind = "bootstrap";
|
|
95
106
|
}
|
|
96
107
|
|
|
@@ -109,6 +120,7 @@ export class BootstrapFetchTransport {
|
|
|
109
120
|
},
|
|
110
121
|
request.timeoutMs
|
|
111
122
|
);
|
|
123
|
+
await this.responseGuard?.(response);
|
|
112
124
|
const body = new Uint8Array(await response.arrayBuffer());
|
|
113
125
|
this.diagnostic?.({
|
|
114
126
|
event_type: "bootstrap_request_completed",
|
|
@@ -279,12 +291,7 @@ export class OysterunLogicalHostConnection {
|
|
|
279
291
|
return retirePromise;
|
|
280
292
|
}
|
|
281
293
|
|
|
282
|
-
changeActiveTransport({
|
|
283
|
-
transport,
|
|
284
|
-
pathKind,
|
|
285
|
-
connectionId,
|
|
286
|
-
reason,
|
|
287
|
-
}) {
|
|
294
|
+
changeActiveTransport({ transport, pathKind, connectionId, reason }) {
|
|
288
295
|
const previousTransport = this.activeTransport;
|
|
289
296
|
const previousPathKind = this.activePathKind;
|
|
290
297
|
if (
|
|
@@ -310,10 +317,7 @@ export class OysterunLogicalHostConnection {
|
|
|
310
317
|
reason,
|
|
311
318
|
});
|
|
312
319
|
|
|
313
|
-
if (
|
|
314
|
-
previousPathKind === "webrtc" &&
|
|
315
|
-
previousTransport !== transport
|
|
316
|
-
) {
|
|
320
|
+
if (previousPathKind === "webrtc" && previousTransport !== transport) {
|
|
317
321
|
return this.retireTransport(previousTransport, {
|
|
318
322
|
transitionGeneration,
|
|
319
323
|
reason,
|
|
@@ -329,6 +333,7 @@ export class OysterunLogicalHostConnection {
|
|
|
329
333
|
requestId = createConnectivityRandomId(),
|
|
330
334
|
syncOwnerId = null,
|
|
331
335
|
timeoutMs = undefined,
|
|
336
|
+
onUploadProgress = null,
|
|
332
337
|
} = {}
|
|
333
338
|
) {
|
|
334
339
|
if (this.closed) throw new Error("Logical Host connection is closed");
|
|
@@ -356,6 +361,7 @@ export class OysterunLogicalHostConnection {
|
|
|
356
361
|
const result = await transport.request(request, {
|
|
357
362
|
requestId,
|
|
358
363
|
signal: request.signal || null,
|
|
364
|
+
onUploadProgress,
|
|
359
365
|
timeoutMs,
|
|
360
366
|
});
|
|
361
367
|
this.emitDiagnostic({
|
|
@@ -417,8 +423,9 @@ export class OysterunLogicalHostConnection {
|
|
|
417
423
|
active_connection_id: this.activeConnectionId,
|
|
418
424
|
transition_generation: this.transitionGeneration,
|
|
419
425
|
transition_state: this.transitionState,
|
|
420
|
-
active_in_flight_request_count:
|
|
421
|
-
this.
|
|
426
|
+
active_in_flight_request_count: this.ensureTransportState(
|
|
427
|
+
this.activeTransport
|
|
428
|
+
).inFlight,
|
|
422
429
|
retiring_transport_count: this.retiringTransports.size,
|
|
423
430
|
matrix_sync_owner_id: this.syncOwnerId,
|
|
424
431
|
closed: this.closed,
|
|
@@ -43,6 +43,26 @@ export function managedHostFetch(
|
|
|
43
43
|
input: RequestInfo | URL,
|
|
44
44
|
init?: RequestInit
|
|
45
45
|
): Promise<Response>;
|
|
46
|
+
export function managedRequest(
|
|
47
|
+
input: RequestInfo | URL,
|
|
48
|
+
init?: RequestInit
|
|
49
|
+
): Promise<Response>;
|
|
50
|
+
export type ManagedBlobUploadInit = Omit<RequestInit, "body"> & {
|
|
51
|
+
body: Blob | ArrayBuffer | Uint8Array | FormData;
|
|
52
|
+
onProgress?: ((progress: { loaded: number; total: number }) => void) | null;
|
|
53
|
+
};
|
|
54
|
+
export function managedBlobUpload(
|
|
55
|
+
input: RequestInfo | URL,
|
|
56
|
+
init: ManagedBlobUploadInit
|
|
57
|
+
): Promise<Response>;
|
|
58
|
+
export function managedBlobDownload(
|
|
59
|
+
input: RequestInfo | URL,
|
|
60
|
+
init?: RequestInit
|
|
61
|
+
): Promise<Response>;
|
|
62
|
+
export function managedMediaObjectUrl(
|
|
63
|
+
input: RequestInfo | URL,
|
|
64
|
+
init?: RequestInit
|
|
65
|
+
): Promise<{ blob: Blob; url: string; revoke(): void }>;
|
|
46
66
|
export function waitForManagedHostAvailability(): Promise<boolean>;
|
|
47
67
|
export function getManagedHostConnectionStatus(): Record<string, unknown>;
|
|
48
68
|
export function subscribeManagedHostConnectionStatus(
|
|
@@ -16,12 +16,18 @@ const BOOTSTRAP_MATRIX_SYNC_TIMEOUT_MS = 45_000;
|
|
|
16
16
|
const PRESENCE_WAIT_SECONDS = 55;
|
|
17
17
|
const PRESENCE_REQUEST_TIMEOUT_MS = 60_000;
|
|
18
18
|
const PRESENCE_FALLBACK_PROBE_MS = 30_000;
|
|
19
|
+
const PRESENCE_HEALTH_RECOVERY_DELAYS_MS = Object.freeze([
|
|
20
|
+
250, 500, 1_000, 2_000,
|
|
21
|
+
]);
|
|
22
|
+
const PRESENCE_HEALTH_RECOVERY_MAX_TOTAL_MS = 10_000;
|
|
19
23
|
const BACKGROUND_PROMOTION_ATTEMPT_LIMIT = 4;
|
|
20
24
|
const BACKGROUND_PROMOTION_WINDOW_MS = 30 * 60 * 1000;
|
|
21
25
|
const HOST_UNAVAILABLE_BODY_MAX_BYTES = 16 * 1024;
|
|
22
26
|
const HOST_UNAVAILABLE_MARKER =
|
|
23
27
|
'<meta name="oysterun-route-state" content="host-unavailable">';
|
|
24
|
-
const
|
|
28
|
+
const HOST_UNAVAILABLE_RESPONSE_ERROR =
|
|
29
|
+
"managed_host_route_unavailable_response";
|
|
30
|
+
const BULK_REQUEST_TIMEOUT_MS = 120_000;
|
|
25
31
|
const MATRIX_SYNC_PATH = /^\/_matrix\/client\/[^/]+\/sync(?:\?|$)/;
|
|
26
32
|
const SIGNALING_PATH_PREFIX = "/connectivity/webrtc/";
|
|
27
33
|
const MANAGED_ENDPOINT_HOSTNAME = /^h-([a-z2-7]{16})\.tunnel\.oysterun\.com$/i;
|
|
@@ -370,6 +376,14 @@ function managedHostUnavailableError() {
|
|
|
370
376
|
return error;
|
|
371
377
|
}
|
|
372
378
|
|
|
379
|
+
function managedHostRouteUnavailableResponseError() {
|
|
380
|
+
const error = new Error(
|
|
381
|
+
"Managed Oysterun Relay reported that the Host route is unavailable"
|
|
382
|
+
);
|
|
383
|
+
error.code = HOST_UNAVAILABLE_RESPONSE_ERROR;
|
|
384
|
+
return error;
|
|
385
|
+
}
|
|
386
|
+
|
|
373
387
|
function managedHostConnectionClosedError() {
|
|
374
388
|
const error = new Error("Managed Oysterun Host browser connection is closed");
|
|
375
389
|
error.code = "managed_host_connection_closed";
|
|
@@ -460,17 +474,6 @@ function withTimeout(promise, timeoutMs, message) {
|
|
|
460
474
|
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
461
475
|
}
|
|
462
476
|
|
|
463
|
-
function bytesToBase64(bytes) {
|
|
464
|
-
let binary = "";
|
|
465
|
-
const chunkBytes = 0x8000;
|
|
466
|
-
for (let offset = 0; offset < bytes.byteLength; offset += chunkBytes) {
|
|
467
|
-
binary += String.fromCharCode(
|
|
468
|
-
...bytes.subarray(offset, Math.min(bytes.byteLength, offset + chunkBytes))
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
return btoa(binary);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
477
|
function base64ToBytes(value) {
|
|
475
478
|
const binary = atob(value);
|
|
476
479
|
const bytes = new Uint8Array(binary.length);
|
|
@@ -811,7 +814,7 @@ export class DirectHostBrowserConnection {
|
|
|
811
814
|
this.notifyStatusListeners();
|
|
812
815
|
}
|
|
813
816
|
|
|
814
|
-
async fetch(input, init = {}) {
|
|
817
|
+
async fetch(input, init = {}, _options = {}) {
|
|
815
818
|
const path = normalizeHostPath(input);
|
|
816
819
|
if (path) recordTransportRequest(this.accounting, "direct", path);
|
|
817
820
|
try {
|
|
@@ -885,6 +888,7 @@ export class ManagedHostBrowserConnection {
|
|
|
885
888
|
ticketProvider = null,
|
|
886
889
|
bootstrapSafeRequestTimeoutMs = BOOTSTRAP_SAFE_REQUEST_TIMEOUT_MS,
|
|
887
890
|
bootstrapMatrixSyncTimeoutMs = BOOTSTRAP_MATRIX_SYNC_TIMEOUT_MS,
|
|
891
|
+
presenceHealthRecoveryDelaysMs = PRESENCE_HEALTH_RECOVERY_DELAYS_MS,
|
|
888
892
|
} = {}) {
|
|
889
893
|
this.origin = origin.replace(/\/+$/, "");
|
|
890
894
|
this.fetchImpl = fetchImpl;
|
|
@@ -898,13 +902,36 @@ export class ManagedHostBrowserConnection {
|
|
|
898
902
|
) {
|
|
899
903
|
throw new Error("Managed Bootstrap request timeouts must be positive");
|
|
900
904
|
}
|
|
905
|
+
if (
|
|
906
|
+
!Array.isArray(presenceHealthRecoveryDelaysMs) ||
|
|
907
|
+
presenceHealthRecoveryDelaysMs.length === 0 ||
|
|
908
|
+
presenceHealthRecoveryDelaysMs.some(
|
|
909
|
+
(delayMs) => !Number.isInteger(delayMs) || delayMs <= 0
|
|
910
|
+
) ||
|
|
911
|
+
presenceHealthRecoveryDelaysMs.reduce(
|
|
912
|
+
(totalMs, delayMs) => totalMs + delayMs,
|
|
913
|
+
0
|
|
914
|
+
) > PRESENCE_HEALTH_RECOVERY_MAX_TOTAL_MS
|
|
915
|
+
) {
|
|
916
|
+
throw new Error(
|
|
917
|
+
"Managed Host presence health recovery delays must be positive integers totaling at most 10000ms"
|
|
918
|
+
);
|
|
919
|
+
}
|
|
901
920
|
this.bootstrapSafeRequestTimeoutMs = bootstrapSafeRequestTimeoutMs;
|
|
902
921
|
this.bootstrapMatrixSyncTimeoutMs = bootstrapMatrixSyncTimeoutMs;
|
|
922
|
+
this.presenceHealthRecoveryDelaysMs = Object.freeze([
|
|
923
|
+
...presenceHealthRecoveryDelaysMs,
|
|
924
|
+
]);
|
|
903
925
|
this.clientInstanceId = getClientInstanceId();
|
|
904
926
|
this.bootstrapTransport = new BootstrapFetchTransport({
|
|
905
927
|
origin: this.origin,
|
|
906
928
|
fetchImpl,
|
|
907
929
|
diagnostic: (event) => this.emitDiagnostic(event),
|
|
930
|
+
responseGuard: async (response) => {
|
|
931
|
+
if (await isRelayHostUnavailableResponse(response)) {
|
|
932
|
+
throw managedHostRouteUnavailableResponseError();
|
|
933
|
+
}
|
|
934
|
+
},
|
|
908
935
|
});
|
|
909
936
|
this.logicalConnection = new OysterunLogicalHostConnection({
|
|
910
937
|
bootstrapTransport: this.bootstrapTransport,
|
|
@@ -1918,13 +1945,36 @@ export class ManagedHostBrowserConnection {
|
|
|
1918
1945
|
return this.immediateRecoveryPromise;
|
|
1919
1946
|
}
|
|
1920
1947
|
|
|
1948
|
+
async attemptBoundedDataPlaneRecovery() {
|
|
1949
|
+
for (const delayMs of this.presenceHealthRecoveryDelaysMs) {
|
|
1950
|
+
await sleep(delayMs);
|
|
1951
|
+
if (
|
|
1952
|
+
!this.routeUnavailable ||
|
|
1953
|
+
this.closed ||
|
|
1954
|
+
globalThis.navigator?.onLine === false
|
|
1955
|
+
) {
|
|
1956
|
+
return false;
|
|
1957
|
+
}
|
|
1958
|
+
if (await this.probeManagedHealth()) {
|
|
1959
|
+
this.completePresenceRecovery();
|
|
1960
|
+
return true;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
return false;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1921
1966
|
async monitorPresenceRecovery() {
|
|
1922
1967
|
while (this.routeUnavailable && !this.closed) {
|
|
1923
1968
|
try {
|
|
1924
1969
|
const presence = await this.fetchPresence();
|
|
1925
|
-
if (presence.route_available
|
|
1926
|
-
this.
|
|
1927
|
-
|
|
1970
|
+
if (presence.route_available) {
|
|
1971
|
+
if (await this.probeManagedHealth()) {
|
|
1972
|
+
this.completePresenceRecovery();
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
if (await this.attemptBoundedDataPlaneRecovery()) {
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1928
1978
|
}
|
|
1929
1979
|
const waited = await this.fetchPresence("/wait", {
|
|
1930
1980
|
after_revision: presence.presence_revision,
|
|
@@ -2049,6 +2099,16 @@ export class ManagedHostBrowserConnection {
|
|
|
2049
2099
|
: null,
|
|
2050
2100
|
});
|
|
2051
2101
|
} catch (error) {
|
|
2102
|
+
if (error?.code === HOST_UNAVAILABLE_RESPONSE_ERROR) {
|
|
2103
|
+
const activated = await this.activateHostUnavailable();
|
|
2104
|
+
if (activated) {
|
|
2105
|
+
if (!isSafeRecoveryMethod(method)) {
|
|
2106
|
+
throw managedHostUnavailableError();
|
|
2107
|
+
}
|
|
2108
|
+
await this.waitForPresenceRecovery(requestSignal(input, init));
|
|
2109
|
+
return this.performFetch(input, init, recovery);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2052
2112
|
const currentPathKind =
|
|
2053
2113
|
this.logicalConnection.status().active_path_kind;
|
|
2054
2114
|
if (
|
|
@@ -2081,16 +2141,6 @@ export class ManagedHostBrowserConnection {
|
|
|
2081
2141
|
logicalResultBodyByteLength(result),
|
|
2082
2142
|
path
|
|
2083
2143
|
);
|
|
2084
|
-
if (await isRelayHostUnavailableResponse(response)) {
|
|
2085
|
-
const activated = await this.activateHostUnavailable();
|
|
2086
|
-
if (activated) {
|
|
2087
|
-
if (!isSafeRecoveryMethod(method)) {
|
|
2088
|
-
throw managedHostUnavailableError();
|
|
2089
|
-
}
|
|
2090
|
-
await this.waitForPresenceRecovery(requestSignal(input, init));
|
|
2091
|
-
return this.performFetch(input, init, recovery);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
2144
|
return response;
|
|
2095
2145
|
}
|
|
2096
2146
|
|
|
@@ -2099,13 +2149,6 @@ export class ManagedHostBrowserConnection {
|
|
|
2099
2149
|
request.method === "GET" || request.method === "HEAD"
|
|
2100
2150
|
? null
|
|
2101
2151
|
: new Uint8Array(await request.arrayBuffer());
|
|
2102
|
-
if (body && body.byteLength > MAX_CONTROL_REQUEST_BODY_BYTES) {
|
|
2103
|
-
recordFallbackReason(this.accounting, "request_body_requires_bootstrap");
|
|
2104
|
-
recordTransportRequest(this.accounting, "bootstrap", path);
|
|
2105
|
-
const response = await this.fetchImpl(input, init);
|
|
2106
|
-
recordNativeResponseBytes(this.accounting, "bootstrap", path, response);
|
|
2107
|
-
return response;
|
|
2108
|
-
}
|
|
2109
2152
|
if (request.signal.aborted) {
|
|
2110
2153
|
throw new DOMException("The operation was aborted.", "AbortError");
|
|
2111
2154
|
}
|
|
@@ -2113,14 +2156,18 @@ export class ManagedHostBrowserConnection {
|
|
|
2113
2156
|
method: request.method,
|
|
2114
2157
|
path,
|
|
2115
2158
|
headers: Object.fromEntries(request.headers.entries()),
|
|
2116
|
-
|
|
2159
|
+
signal: request.signal,
|
|
2160
|
+
...(body ? { body } : {}),
|
|
2117
2161
|
};
|
|
2118
2162
|
const result = await this.logicalConnection.request(requestPayload, {
|
|
2119
2163
|
requestId: createConnectivityRandomId(),
|
|
2120
2164
|
syncOwnerId: MATRIX_SYNC_PATH.test(path) ? this.ensureSyncOwner() : null,
|
|
2121
2165
|
timeoutMs: MATRIX_SYNC_PATH.test(path)
|
|
2122
2166
|
? this.bootstrapMatrixSyncTimeoutMs
|
|
2167
|
+
: body?.byteLength > 0
|
|
2168
|
+
? BULK_REQUEST_TIMEOUT_MS
|
|
2123
2169
|
: undefined,
|
|
2170
|
+
onUploadProgress: recovery.onUploadProgress || null,
|
|
2124
2171
|
});
|
|
2125
2172
|
recordTransportRequest(this.accounting, "datachannel", path);
|
|
2126
2173
|
recordTransportResponseBytes(
|
|
@@ -2224,6 +2271,53 @@ export function managedHostFetch(input, init) {
|
|
|
2224
2271
|
return getManagedHostBrowserConnection().fetch(input, init);
|
|
2225
2272
|
}
|
|
2226
2273
|
|
|
2274
|
+
export function managedRequest(input, init) {
|
|
2275
|
+
return managedHostFetch(input, init);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
export function managedBlobUpload(
|
|
2279
|
+
input,
|
|
2280
|
+
{ body, onProgress = null, ...init } = {}
|
|
2281
|
+
) {
|
|
2282
|
+
if (
|
|
2283
|
+
!(body instanceof Blob) &&
|
|
2284
|
+
!(body instanceof ArrayBuffer) &&
|
|
2285
|
+
!(body instanceof Uint8Array) &&
|
|
2286
|
+
!(body instanceof FormData)
|
|
2287
|
+
) {
|
|
2288
|
+
throw new TypeError(
|
|
2289
|
+
"Managed blob upload requires Blob, bytes, or FormData"
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
return getManagedHostBrowserConnection().fetch(
|
|
2293
|
+
input,
|
|
2294
|
+
{ ...init, body },
|
|
2295
|
+
{ onUploadProgress: onProgress }
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
export function managedBlobDownload(input, init = {}) {
|
|
2300
|
+
return managedHostFetch(input, { ...init, method: init.method || "GET" });
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
export async function managedMediaObjectUrl(input, init = {}) {
|
|
2304
|
+
const response = await managedBlobDownload(input, init);
|
|
2305
|
+
if (!response.ok) {
|
|
2306
|
+
throw new Error(
|
|
2307
|
+
`Managed media download failed with HTTP ${response.status}`
|
|
2308
|
+
);
|
|
2309
|
+
}
|
|
2310
|
+
const blob = await response.blob();
|
|
2311
|
+
const url = URL.createObjectURL(blob);
|
|
2312
|
+
return {
|
|
2313
|
+
blob,
|
|
2314
|
+
url,
|
|
2315
|
+
revoke() {
|
|
2316
|
+
URL.revokeObjectURL(url);
|
|
2317
|
+
},
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2227
2321
|
export function waitForManagedHostAvailability() {
|
|
2228
2322
|
const connection = getManagedHostBrowserConnection();
|
|
2229
2323
|
if (
|