peer-kit / src / ardegazu / peer / core / 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
;; ported-from: src/core/turn.ts @ v1.3.0
;;
;; Fetches ephemeral TURN credentials from the signaling host (coturn REST
;; scheme) and builds RTCConfiguration. Caches until 5 minutes before expiry.
(ns ardegazu.peer.core.turn
  (:require [shadow.cljs.modern :refer (defclass js-await)]))

(defclass IceConfig
  (constructor [this endpoint force-relay]
    (unchecked-set this "_endpoint" endpoint)
    ;; `forceRelay = false` was a JS default parameter
    (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- ice-fetch [self]
  (-> (js/Promise.resolve nil)
      (.then
       (fn [_]
         (js-await [res (js/fetch (unchecked-get self "_endpoint"))]
           (do
             (when-not (.-ok ^js res)
               (throw (js/Error. (str "turn-credentials " (.-status ^js res)))))
             (js-await [c (.json ^js res)]
               (let [cfg (js-obj "iceServers"
                                 #js [(js-obj "urls" (unchecked-get c "urls")
                                              "username" (unchecked-get c "username")
                                              "credential" (unchecked-get c "credential"))])]
                 (when (unchecked-get self "_forceRelay")
                   (unchecked-set cfg "iceTransportPolicy" "relay"))
                 (unchecked-set self "_cached" cfg)
                 ;; renew 5 min before expiry
                 (unchecked-set self "_validUntil"
                                (- (+ (js/Date.now) (* (unchecked-get c "ttl") 1000)) 300000))
                 cfg))))))
      (.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 (unchecked-get self "_cached")
           (unchecked-set self "_cached"
                          (if (unchecked-get self "_forceRelay")
                            (js-obj "iceTransportPolicy" "relay")
                            (js-obj))))
         (unchecked-set self "_validUntil" (+ (js/Date.now) 30000)) ; retry soon, don't storm
         (unchecked-get self "_cached")))))

(defn ice-get [self]
  (if (and (unchecked-get self "_cached")
           (< (js/Date.now) (unchecked-get self "_validUntil")))
    (js/Promise.resolve (unchecked-get self "_cached"))
    (do
      ;; `#inflight ??= ...` — one in-flight fetch shared by every caller
      (when-not (unchecked-get self "_inflight")
        (unchecked-set self "_inflight"
                       (.finally (ice-fetch self)
                                 (fn [] (unchecked-set self "_inflight" nil)))))
      (unchecked-get self "_inflight"))))

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

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