rooms-kit / src / ardegazu / rooms / lib / turn.cljs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;; ported-from: src/lib/turn.ts @ v1.0.0
;;
;; Fetches ephemeral TURN credentials from the signaling host (coturn REST
;; scheme) and builds RTCConfiguration. Caches until 5 minutes before expiry.
(ns ardegazu.rooms.lib.turn
  (:require [ardegazu.rooms.js :as j]
            [shadow.cljs.modern :refer (defclass js-await)]))

(defclass IceConfig
  (constructor [this endpoint force-relay]
    (unchecked-set this "_endpoint" endpoint)
    ;; TS default parameter: `forceRelay = false`
    (unchecked-set this "_forceRelay" (if (identical? force-relay js/undefined) false force-relay))
    (unchecked-set this "_cached" nil)
    (unchecked-set this "_validUntil" 0) ; refetch after this timestamp
    (unchecked-set this "_inflight" nil)))

(defn- do-fetch [self]
  (-> (js/Promise.resolve nil)
      (.then
       (fn [_]
         (js-await [res (js/fetch (unchecked-get self "_endpoint"))]
           (if-not (j/truthy? (unchecked-get res "ok"))
             (throw (js/Error. (str "turn-credentials " (unchecked-get res "status"))))
             (js-await [c (.json ^js res)]
               (do
                 (unchecked-set
                  self "_cached"
                  (let [out (j/ordered
                             "iceServers" #js [(j/ordered "urls" (unchecked-get c "urls")
                                                          "username" (unchecked-get c "username")
                                                          "credential" (unchecked-get c "credential"))])]
                    (when (j/truthy? (unchecked-get self "_forceRelay"))
                      (unchecked-set out "iceTransportPolicy" "relay"))
                    out))
                 ;; renew 5 min before expiry
                 (unchecked-set self "_validUntil"
                                (- (+ (js/Date.now) (* (unchecked-get c "ttl") 1000)) 300000))
                 js/undefined))))))
      (.catch
       (fn [err]
         ;; Credential fetch failing shouldn't block LAN/host-candidate connections.
         (js/console.warn "turn credentials unavailable, continuing without ICE servers" err)
         (when-not (j/truthy? (unchecked-get self "_cached"))
           (unchecked-set self "_cached"
                          (if (j/truthy? (unchecked-get self "_forceRelay"))
                            (j/ordered "iceTransportPolicy" "relay")
                            (j/ordered))))
         ;; retry soon, but don't storm
         (unchecked-set self "_validUntil" (+ (js/Date.now) 30000))
         js/undefined))
      (.then (fn [_] (unchecked-get self "_cached")))))

(defn get-config [self]
  (if (and (j/truthy? (unchecked-get self "_cached"))
           (< (js/Date.now) (unchecked-get self "_validUntil")))
    (js/Promise.resolve (unchecked-get self "_cached"))
    (do
      ;; TS `this.#inflight ??= this.#fetch().finally(() => (this.#inflight = null))`
      (when-not (j/truthy? (unchecked-get self "_inflight"))
        (unchecked-set self "_inflight"
                       (.finally (do-fetch self)
                                 (fn [] (unchecked-set self "_inflight" nil)))))
      (unchecked-get self "_inflight"))))

(unchecked-set (.-prototype IceConfig) "get" (fn [] (this-as self (get-config self))))

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/rooms-kit.git