From a0418d26ec685e58e3c78c8895074f5d56016e41 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Mon, 28 Dec 2020 02:25:46 +0100 Subject: [PATCH] Send RTC configuration with joined message. This avoids one HTTP request, and is potentially more flexible. --- data/ice-servers.json | 1 - galene.go | 2 +- group/group.go | 28 --------------- group/ice.go | 78 ++++++++++++++++++++++++++++++++++++++++++ rtpconn/rtpconn.go | 8 +++-- rtpconn/webclient.go | 55 +++++++++++++++-------------- static/protocol.js | 44 ++++-------------------- webserver/webserver.go | 6 ---- 8 files changed, 121 insertions(+), 101 deletions(-) delete mode 100644 data/ice-servers.json create mode 100644 group/ice.go diff --git a/data/ice-servers.json b/data/ice-servers.json deleted file mode 100644 index fe51488..0000000 --- a/data/ice-servers.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/galene.go b/galene.go index 9ce9a57..cf537eb 100644 --- a/galene.go +++ b/galene.go @@ -79,7 +79,7 @@ func main() { }() } - group.IceFilename = filepath.Join(dataDir, "ice-servers.json") + group.ICEFilename = filepath.Join(dataDir, "ice-servers.json") go group.ReadPublicGroups() diff --git a/group/group.go b/group/group.go index 6a232c9..d124dbb 100644 --- a/group/group.go +++ b/group/group.go @@ -50,34 +50,6 @@ func (err ProtocolError) Error() string { return string(err) } -var IceFilename string - -var iceConf webrtc.Configuration -var iceOnce sync.Once - -func IceConfiguration() webrtc.Configuration { - iceOnce.Do(func() { - var iceServers []webrtc.ICEServer - file, err := os.Open(IceFilename) - if err != nil { - log.Printf("Open %v: %v", IceFilename, err) - return - } - defer file.Close() - d := json.NewDecoder(file) - err = d.Decode(&iceServers) - if err != nil { - log.Printf("Get ICE configuration: %v", err) - return - } - iceConf = webrtc.Configuration{ - ICEServers: iceServers, - } - }) - - return iceConf -} - type ChatHistoryEntry struct { Id string User string diff --git a/group/ice.go b/group/ice.go new file mode 100644 index 0000000..c79d8d2 --- /dev/null +++ b/group/ice.go @@ -0,0 +1,78 @@ +package group + +import ( + "encoding/json" + "log" + "os" + "sync" + + "github.com/pion/webrtc/v3" +) + +type ICEServer struct { + URLs []string `json:"urls"` + Username string `json:"username,omitempty"` + Credential interface{} `json:"credential,omitempty"` + CredentialType string `json:"credentialType,omitempty"` +} + +type RTCConfiguration struct { + ICEServers []ICEServer `json:"iceServers,omitempty"` + ICETransportPolicy string `json:"iceTransportPolicy,omitempty"` +} + +var ICEFilename string + +var iceConf RTCConfiguration +var iceOnce sync.Once + +func ICEConfiguration() *RTCConfiguration { + iceOnce.Do(func() { + var iceServers []ICEServer + file, err := os.Open(ICEFilename) + if err != nil { + log.Printf("Open %v: %v", ICEFilename, err) + return + } + defer file.Close() + d := json.NewDecoder(file) + err = d.Decode(&iceServers) + if err != nil { + log.Printf("Get ICE configuration: %v", err) + return + } + iceConf = RTCConfiguration{ + ICEServers: iceServers, + } + }) + + return &iceConf +} + +func ToConfiguration(conf *RTCConfiguration) webrtc.Configuration { + var iceServers []webrtc.ICEServer + for _, s := range conf.ICEServers { + tpe := webrtc.ICECredentialTypePassword + if s.CredentialType == "oauth" { + tpe = webrtc.ICECredentialTypeOauth + } + iceServers = append(iceServers, + webrtc.ICEServer{ + URLs: s.URLs, + Username: s.Username, + Credential: s.Credential, + CredentialType: tpe, + }, + ) + } + + policy := webrtc.ICETransportPolicyAll + if conf.ICETransportPolicy == "relay" { + policy = webrtc.ICETransportPolicyRelay + } + + return webrtc.Configuration{ + ICEServers: iceServers, + ICETransportPolicy: policy, + } +} diff --git a/rtpconn/rtpconn.go b/rtpconn/rtpconn.go index 9212f82..8cfdb0b 100644 --- a/rtpconn/rtpconn.go +++ b/rtpconn/rtpconn.go @@ -138,7 +138,9 @@ type rtpDownConnection struct { func newDownConn(c group.Client, id string, remote conn.Up) (*rtpDownConnection, error) { api := group.APIFromCodecs(remote.Codecs()) - pc, err := api.NewPeerConnection(group.IceConfiguration()) + pc, err := api.NewPeerConnection( + group.ToConfiguration(group.ICEConfiguration()), + ) if err != nil { return nil, err } @@ -457,7 +459,9 @@ func pushConn(up *rtpUpConnection, g *group.Group, cs []group.Client) { } func newUpConn(c group.Client, id string, labels map[string]string) (*rtpUpConnection, error) { - pc, err := c.Group().API().NewPeerConnection(group.IceConfiguration()) + pc, err := c.Group().API().NewPeerConnection( + group.ToConfiguration(group.ICEConfiguration()), + ) if err != nil { return nil, err } diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index 9b07c16..2d03a03 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -161,22 +161,23 @@ func (v rateMap) MarshalJSON() ([]byte, error) { } type clientMessage struct { - Type string `json:"type"` - Kind string `json:"kind,omitempty"` - Id string `json:"id,omitempty"` - Dest string `json:"dest,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - Privileged bool `json:"privileged,omitempty"` - Permissions *group.ClientPermissions `json:"permissions,omitempty"` - Group string `json:"group,omitempty"` - Value interface{} `json:"value,omitempty"` - Time int64 `json:"time,omitempty"` - Offer *webrtc.SessionDescription `json:"offer,omitempty"` - Answer *webrtc.SessionDescription `json:"answer,omitempty"` - Candidate *webrtc.ICECandidateInit `json:"candidate,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Request rateMap `json:"request,omitempty"` + Type string `json:"type"` + Kind string `json:"kind,omitempty"` + Id string `json:"id,omitempty"` + Dest string `json:"dest,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Privileged bool `json:"privileged,omitempty"` + Permissions *group.ClientPermissions `json:"permissions,omitempty"` + Group string `json:"group,omitempty"` + Value interface{} `json:"value,omitempty"` + Time int64 `json:"time,omitempty"` + Offer *webrtc.SessionDescription `json:"offer,omitempty"` + Answer *webrtc.SessionDescription `json:"answer,omitempty"` + Candidate *webrtc.ICECandidateInit `json:"candidate,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Request rateMap `json:"request,omitempty"` + RTCConfiguration *group.RTCConfiguration `json:"rtcConfiguration,omitempty"` } type closeMessage struct { @@ -866,16 +867,17 @@ func clientLoop(c *webClient, ws *websocket.Conn) error { } case permissionsChangedAction: - group := c.Group() - if group == nil { + g := c.Group() + if g == nil { return errors.New("Permissions changed in no group") } perms := c.permissions c.write(clientMessage{ - Type: "joined", - Kind: "change", - Group: group.Name(), - Permissions: &perms, + Type: "joined", + Kind: "change", + Group: g.Name(), + Permissions: &perms, + RTCConfiguration: group.ICEConfiguration(), }) if !c.permissions.Present { up := getUpConns(c) @@ -1078,10 +1080,11 @@ func handleClientMessage(c *webClient, m clientMessage) error { c.group = g perms := c.permissions err = c.write(clientMessage{ - Type: "joined", - Kind: "join", - Group: m.Group, - Permissions: &perms, + Type: "joined", + Kind: "join", + Group: m.Group, + Permissions: &perms, + RTCConfiguration: group.ICEConfiguration(), }) if err != nil { return err diff --git a/static/protocol.js b/static/protocol.js index 9a67668..65ebc2d 100644 --- a/static/protocol.js +++ b/static/protocol.js @@ -85,9 +85,9 @@ function ServerConnection() { /** * The ICE configuration used by all associated streams. * - * @type {RTCIceServer[]} + * @type {RTCConfiguration} */ - this.iceServers = null; + this.rtcConfiguration = null; /** * The permissions granted to this connection. * @@ -183,6 +183,7 @@ function ServerConnection() { * @property {RTCIceCandidate} [candidate] * @property {Object} [labels] * @property {Object} [request] + * @property {Object} [rtcConfiguration] */ /** @@ -206,26 +207,6 @@ ServerConnection.prototype.send = function(m) { return this.socket.send(JSON.stringify(m)); } -/** - * getIceServers fetches an ICE configuration from the server and - * populates the iceServers field of a ServerConnection. It is called - * lazily by connect. - * - * @returns {Promise} - * @function - */ -ServerConnection.prototype.getIceServers = async function() { - let r = await fetch('/ice-servers.json'); - if(!r.ok) - throw new Error("Couldn't fetch ICE servers: " + - r.status + ' ' + r.statusText); - let servers = await r.json(); - if(!(servers instanceof Array)) - throw new Error("couldn't parse ICE servers"); - this.iceServers = servers; - return servers; -} - /** * connect connects to the server. * @@ -240,14 +221,6 @@ ServerConnection.prototype.connect = async function(url) { sc.socket = null; } - if(!sc.iceServers) { - try { - await sc.getIceServers(); - } catch(e) { - console.warn(e); - } - } - sc.socket = new WebSocket(url); return await new Promise((resolve, reject) => { @@ -311,7 +284,8 @@ ServerConnection.prototype.connect = async function(url) { } else { sc.group = m.group; } - sc.permissions = m.permissions; + sc.permissions = m.permissions || []; + sc.rtcConfiguration = m.rtcConfiguration || null; if(sc.onjoined) sc.onjoined.call(sc, m.kind, m.group, m.permissions || {}, @@ -432,9 +406,7 @@ ServerConnection.prototype.newUpStream = function(id) { if(sc.up[id]) throw new Error('Eek!'); } - let pc = new RTCPeerConnection({ - iceServers: sc.iceServers || [], - }); + let pc = new RTCPeerConnection(sc.rtcConfiguration); if(!pc) throw new Error("Couldn't create peer connection"); if(sc.up[id]) { @@ -568,9 +540,7 @@ ServerConnection.prototype.gotOffer = async function(id, labels, offer, renegoti throw new Error('Duplicate connection id'); if(!c) { - let pc = new RTCPeerConnection({ - iceServers: this.iceServers, - }); + let pc = new RTCPeerConnection(sc.rtcConfiguration); c = new Stream(this, id, pc, false); sc.down[id] = c; diff --git a/webserver/webserver.go b/webserver/webserver.go index 8ab8ad7..1fc845a 100644 --- a/webserver/webserver.go +++ b/webserver/webserver.go @@ -45,12 +45,6 @@ func Serve(address string, dataDir string) error { }) http.HandleFunc("/recordings/", recordingsHandler) http.HandleFunc("/ws", wsHandler) - http.HandleFunc("/ice-servers.json", - func(w http.ResponseWriter, r *http.Request) { - mungeHeader(w) - serveFile(w, r, - filepath.Join(dataDir, "ice-servers.json")) - }) http.HandleFunc("/public-groups.json", publicHandler) http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) { statsHandler(w, r, dataDir)