diff --git a/client.go b/client.go deleted file mode 100644 index d9d30f5..0000000 --- a/client.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "sfu/conn" -) - -type clientCredentials struct { - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` -} - -type clientPermissions struct { - Op bool `json:"op,omitempty"` - Present bool `json:"present,omitempty"` - Record bool `json:"record,omitempty"` -} - -type client interface { - Group() *group - Id() string - Credentials() clientCredentials - SetPermissions(clientPermissions) - pushConn(id string, conn conn.Up, tracks []conn.UpTrack, label string) error - pushClient(id, username string, add bool) error -} - -type kickable interface { - kick(message string) error -} diff --git a/disk.go b/disk.go index ea9f922..41f6c03 100644 --- a/disk.go +++ b/disk.go @@ -16,10 +16,11 @@ import ( "github.com/pion/webrtc/v3/pkg/media/samplebuilder" "sfu/conn" + "sfu/group" ) type diskClient struct { - group *group + group *group.Group id string mu sync.Mutex @@ -41,11 +42,11 @@ func newId() string { return s } -func NewDiskClient(g *group) *diskClient { +func NewDiskClient(g *group.Group) *diskClient { return &diskClient{group: g, id: newId()} } -func (client *diskClient) Group() *group { +func (client *diskClient) Group() *group.Group { return client.group } @@ -53,15 +54,15 @@ func (client *diskClient) Id() string { return client.id } -func (client *diskClient) Credentials() clientCredentials { - return clientCredentials{"RECORDING", ""} +func (client *diskClient) Credentials() group.ClientCredentials { + return group.ClientCredentials{"RECORDING", ""} } -func (client *diskClient) SetPermissions(perms clientPermissions) { +func (client *diskClient) SetPermissions(perms group.ClientPermissions) { return } -func (client *diskClient) pushClient(id, username string, add bool) error { +func (client *diskClient) PushClient(id, username string, add bool) error { return nil } @@ -79,11 +80,11 @@ func (client *diskClient) Close() error { func (client *diskClient) kick(message string) error { err := client.Close() - delClient(client) + group.DelClient(client) return err } -func (client *diskClient) pushConn(id string, up conn.Up, tracks []conn.UpTrack, label string) error { +func (client *diskClient) PushConn(id string, up conn.Up, tracks []conn.UpTrack, label string) error { client.mu.Lock() defer client.mu.Unlock() @@ -101,7 +102,7 @@ func (client *diskClient) pushConn(id string, up conn.Up, tracks []conn.UpTrack, return nil } - directory := filepath.Join(recordingsDir, client.group.name) + directory := filepath.Join(recordingsDir, client.group.Name()) err := os.MkdirAll(directory, 0700) if err != nil { return err diff --git a/group/client.go b/group/client.go new file mode 100644 index 0000000..5068c42 --- /dev/null +++ b/group/client.go @@ -0,0 +1,29 @@ +package group + +import ( + "sfu/conn" +) + +type ClientCredentials struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` +} + +type ClientPermissions struct { + Op bool `json:"op,omitempty"` + Present bool `json:"present,omitempty"` + Record bool `json:"record,omitempty"` +} + +type Client interface { + Group() *Group + Id() string + Credentials() ClientCredentials + SetPermissions(ClientPermissions) + PushConn(id string, conn conn.Up, tracks []conn.UpTrack, label string) error + PushClient(id, username string, add bool) error +} + +type Kickable interface { + Kick(message string) error +} diff --git a/group.go b/group/group.go similarity index 64% rename from group.go rename to group/group.go index 75502c4..ff4c85e 100644 --- a/group.go +++ b/group/group.go @@ -3,7 +3,7 @@ // This is not open source software. Copy it, and I'll break into your // house and tell your three year-old that Santa doesn't exist. -package main +package group import ( "encoding/json" @@ -18,18 +18,60 @@ import ( "github.com/pion/webrtc/v3" ) -type chatHistoryEntry struct { - id string - user string - kind string - value string +var Directory string + +type UserError string + +func (err UserError) Error() string { + return string(err) +} + +type ProtocolError string + +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 + Kind string + Value string } const ( - minBitrate = 200000 + MinBitrate = 200000 ) -type group struct { +type Group struct { name string mu sync.Mutex @@ -37,64 +79,60 @@ type group struct { // indicates that the group no longer exists, but it still has clients dead bool locked bool - clients map[string]client - history []chatHistoryEntry + clients map[string]Client + history []ChatHistoryEntry } -func (g *group) Locked() bool { +func (g *Group) Name() string { + return g.name +} + +func (g *Group) Locked() bool { g.mu.Lock() defer g.mu.Unlock() return g.locked } -func (g *group) SetLocked(locked bool) { +func (g *Group) SetLocked(locked bool) { g.mu.Lock() defer g.mu.Unlock() g.locked = locked } -func (g *group) Public() bool { +func (g *Group) Public() bool { g.mu.Lock() defer g.mu.Unlock() return g.description.Public } -func (g *group) Redirect() string { +func (g *Group) Redirect() string { g.mu.Lock() defer g.mu.Unlock() return g.description.Redirect } -func (g *group) AllowRecording() bool { +func (g *Group) AllowRecording() bool { g.mu.Lock() defer g.mu.Unlock() return g.description.AllowRecording } -func (g *group) Redirect() string { - return g.description.Redirect -} - -func (g *group) AllowRecording() bool { - return g.description.AllowRecording -} - var groups struct { mu sync.Mutex - groups map[string]*group + groups map[string]*Group api *webrtc.API } -func (g *group) API() *webrtc.API { +func (g *Group) API() *webrtc.API { return groups.api } -func addGroup(name string, desc *groupDescription) (*group, error) { +func Add(name string, desc *groupDescription) (*Group, error) { groups.mu.Lock() defer groups.mu.Unlock() if groups.groups == nil { - groups.groups = make(map[string]*group) + groups.groups = make(map[string]*Group) s := webrtc.SettingEngine{} m := webrtc.MediaEngine{} m.RegisterCodec(webrtc.NewRTPVP8CodecExt( @@ -121,15 +159,15 @@ func addGroup(name string, desc *groupDescription) (*group, error) { g := groups.groups[name] if g == nil { if desc == nil { - desc, err = getDescription(name) + desc, err = GetDescription(name) if err != nil { return nil, err } } - g = &group{ + g = &Group{ name: name, description: desc, - clients: make(map[string]client), + clients: make(map[string]Client), } groups.groups[name] = g return g, nil @@ -155,7 +193,7 @@ func addGroup(name string, desc *groupDescription) (*group, error) { return nil, err } if changed { - desc, err := getDescription(name) + desc, err := GetDescription(name) if err != nil { if !os.IsNotExist(err) { log.Printf("Reading group %v: %v", @@ -175,7 +213,7 @@ func addGroup(name string, desc *groupDescription) (*group, error) { return g, nil } -func rangeGroups(f func(g *group) bool) { +func Range(f func(g *Group) bool) { groups.mu.Lock() defer groups.mu.Unlock() @@ -187,17 +225,17 @@ func rangeGroups(f func(g *group) bool) { } } -func getGroupNames() []string { +func GetNames() []string { names := make([]string, 0) - rangeGroups(func(g *group) bool { + Range(func(g *Group) bool { names = append(names, g.name) return true }) return names } -func getGroup(name string) *group { +func Get(name string) *Group { groups.mu.Lock() defer groups.mu.Unlock() @@ -218,8 +256,8 @@ func delGroupUnlocked(name string) bool { return true } -func addClient(name string, c client) (*group, error) { - g, err := addGroup(name, nil) +func AddClient(name string, c Client) (*Group, error) { + g, err := Add(name, nil) if err != nil { return nil, err } @@ -227,7 +265,7 @@ func addClient(name string, c client) (*group, error) { g.mu.Lock() defer g.mu.Unlock() - perms, err := getPermission(g.description, c.Credentials()) + perms, err := g.description.GetPermission(c.Credentials()) if err != nil { return nil, err } @@ -235,37 +273,34 @@ func addClient(name string, c client) (*group, error) { c.SetPermissions(perms) if !perms.Op && g.locked { - return nil, userError("group is locked") + return nil, UserError("group is locked") } if !perms.Op && g.description.MaxClients > 0 { if len(g.clients) >= g.description.MaxClients { - return nil, userError("too many users") + return nil, UserError("too many users") } } if g.clients[c.Id()] != nil { - return nil, protocolError("duplicate client id") + return nil, ProtocolError("duplicate client id") } g.clients[c.Id()] = c - go func(clients []client) { + go func(clients []Client) { u := c.Credentials().Username - c.pushClient(c.Id(), u, true) + c.PushClient(c.Id(), u, true) for _, cc := range clients { uu := cc.Credentials().Username - err := c.pushClient(cc.Id(), uu, true) - if err == ErrClientDead { - return - } - cc.pushClient(c.Id(), u, true) + c.PushClient(cc.Id(), uu, true) + cc.PushClient(c.Id(), u, true) } }(g.getClientsUnlocked(c)) return g, nil } -func delClient(c client) { +func DelClient(c Client) { g := c.Group() if g == nil { return @@ -279,21 +314,21 @@ func delClient(c client) { } delete(g.clients, c.Id()) - go func(clients []client) { + go func(clients []Client) { for _, cc := range clients { - cc.pushClient(c.Id(), c.Credentials().Username, false) + cc.PushClient(c.Id(), c.Credentials().Username, false) } }(g.getClientsUnlocked(nil)) } -func (g *group) getClients(except client) []client { +func (g *Group) GetClients(except Client) []Client { g.mu.Lock() defer g.mu.Unlock() return g.getClientsUnlocked(except) } -func (g *group) getClientsUnlocked(except client) []client { - clients := make([]client, 0, len(g.clients)) +func (g *Group) getClientsUnlocked(except Client) []Client { + clients := make([]Client, 0, len(g.clients)) for _, c := range g.clients { if c != except { clients = append(clients, c) @@ -302,13 +337,13 @@ func (g *group) getClientsUnlocked(except client) []client { return clients } -func (g *group) getClient(id string) client { +func (g *Group) GetClient(id string) Client { g.mu.Lock() defer g.mu.Unlock() return g.getClientUnlocked(id) } -func (g *group) getClientUnlocked(id string) client { +func (g *Group) getClientUnlocked(id string) Client { for idd, c := range g.clients { if idd == id { return c @@ -317,7 +352,7 @@ func (g *group) getClientUnlocked(id string) client { return nil } -func (g *group) Range(f func(c client) bool) { +func (g *Group) Range(f func(c Client) bool) { g.mu.Lock() defer g.mu.Unlock() for _, c := range g.clients { @@ -328,11 +363,11 @@ func (g *group) Range(f func(c client) bool) { } } -func (g *group) shutdown(message string) { - g.Range(func(c client) bool { - cc, ok := c.(kickable) +func (g *Group) Shutdown(message string) { + g.Range(func(c Client) bool { + cc, ok := c.(Kickable) if ok { - cc.kick(message) + cc.Kick(message) } return true }) @@ -340,13 +375,13 @@ func (g *group) shutdown(message string) { const maxChatHistory = 20 -func (g *group) clearChatHistory() { +func (g *Group) ClearChatHistory() { g.mu.Lock() defer g.mu.Unlock() g.history = nil } -func (g *group) addToChatHistory(id, user, kind, value string) { +func (g *Group) AddToChatHistory(id, user, kind, value string) { g.mu.Lock() defer g.mu.Unlock() @@ -355,20 +390,20 @@ func (g *group) addToChatHistory(id, user, kind, value string) { g.history = g.history[:len(g.history)-1] } g.history = append(g.history, - chatHistoryEntry{id: id, user: user, kind: kind, value: value}, + ChatHistoryEntry{Id: id, User: user, Kind: kind, Value: value}, ) } -func (g *group) getChatHistory() []chatHistoryEntry { +func (g *Group) GetChatHistory() []ChatHistoryEntry { g.mu.Lock() defer g.mu.Unlock() - h := make([]chatHistoryEntry, len(g.history)) + h := make([]ChatHistoryEntry, len(g.history)) copy(h, g.history) return h } -func matchUser(user clientCredentials, users []clientCredentials) (bool, bool) { +func matchUser(user ClientCredentials, users []ClientCredentials) (bool, bool) { for _, u := range users { if u.Username == "" { if u.Password == "" || u.Password == user.Password { @@ -391,13 +426,13 @@ type groupDescription struct { MaxClients int `json:"max-clients,omitempty"` AllowAnonymous bool `json:"allow-anonymous,omitempty"` AllowRecording bool `json:"allow-recording,omitempty"` - Op []clientCredentials `json:"op,omitempty"` - Presenter []clientCredentials `json:"presenter,omitempty"` - Other []clientCredentials `json:"other,omitempty"` + Op []ClientCredentials `json:"op,omitempty"` + Presenter []ClientCredentials `json:"presenter,omitempty"` + Other []ClientCredentials `json:"other,omitempty"` } func descriptionChanged(name string, old *groupDescription) (bool, error) { - fi, err := os.Stat(filepath.Join(groupsDir, name+".json")) + fi, err := os.Stat(filepath.Join(Directory, name+".json")) if err != nil { return false, err } @@ -407,8 +442,8 @@ func descriptionChanged(name string, old *groupDescription) (bool, error) { return false, err } -func getDescription(name string) (*groupDescription, error) { - r, err := os.Open(filepath.Join(groupsDir, name+".json")) +func GetDescription(name string) (*groupDescription, error) { + r, err := os.Open(filepath.Join(Directory, name+".json")) if err != nil { return nil, err } @@ -432,10 +467,10 @@ func getDescription(name string) (*groupDescription, error) { return &desc, nil } -func getPermission(desc *groupDescription, creds clientCredentials) (clientPermissions, error) { - var p clientPermissions +func (desc *groupDescription) GetPermission (creds ClientCredentials) (ClientPermissions, error) { + var p ClientPermissions if !desc.AllowAnonymous && creds.Username == "" { - return p, userError("anonymous users not allowed in this group, please choose a username") + return p, UserError("anonymous users not allowed in this group, please choose a username") } if found, good := matchUser(creds, desc.Op); found { if good { @@ -446,34 +481,34 @@ func getPermission(desc *groupDescription, creds clientCredentials) (clientPermi } return p, nil } - return p, userError("not authorised") + return p, UserError("not authorised") } if found, good := matchUser(creds, desc.Presenter); found { if good { p.Present = true return p, nil } - return p, userError("not authorised") + return p, UserError("not authorised") } if found, good := matchUser(creds, desc.Other); found { if good { return p, nil } - return p, userError("not authorised") + return p, UserError("not authorised") } - return p, userError("not authorised") + return p, UserError("not authorised") } -type publicGroup struct { +type Public struct { Name string `json:"name"` ClientCount int `json:"clientCount"` } -func getPublicGroups() []publicGroup { - gs := make([]publicGroup, 0) - rangeGroups(func(g *group) bool { +func GetPublic() []Public { + gs := make([]Public, 0) + Range(func(g *Group) bool { if g.Public() { - gs = append(gs, publicGroup{ + gs = append(gs, Public{ Name: g.name, ClientCount: len(g.clients), }) @@ -486,8 +521,8 @@ func getPublicGroups() []publicGroup { return gs } -func readPublicGroups() { - dir, err := os.Open(groupsDir) +func ReadPublicGroups() { + dir, err := os.Open(Directory) if err != nil { return } @@ -504,7 +539,7 @@ func readPublicGroups() { continue } name := fi.Name()[:len(fi.Name())-5] - desc, err := getDescription(name) + desc, err := GetDescription(name) if err != nil { if !os.IsNotExist(err) { log.Printf("Reading group %v: %v", name, err) @@ -512,7 +547,7 @@ func readPublicGroups() { continue } if desc.Public { - addGroup(name, desc) + Add(name, desc) } } } diff --git a/rtpconn.go b/rtpconn.go index 193405f..0e42737 100644 --- a/rtpconn.go +++ b/rtpconn.go @@ -20,6 +20,7 @@ import ( "sfu/conn" "sfu/estimator" + "sfu/group" "sfu/jitter" "sfu/packetcache" "sfu/rtptime" @@ -110,8 +111,8 @@ type rtpDownConnection struct { iceCandidates []*webrtc.ICECandidateInit } -func newDownConn(c client, id string, remote conn.Up) (*rtpDownConnection, error) { - pc, err := c.Group().API().NewPeerConnection(iceConfiguration()) +func newDownConn(c group.Client, id string, remote conn.Up) (*rtpDownConnection, error) { + pc, err := c.Group().API().NewPeerConnection(group.IceConfiguration()) if err != nil { return nil, err } @@ -371,8 +372,8 @@ func (up *rtpUpConnection) complete() bool { return true } -func newUpConn(c client, id string) (*rtpUpConnection, error) { - pc, err := c.Group().API().NewPeerConnection(iceConfiguration()) +func newUpConn(c group.Client, id string) (*rtpUpConnection, error) { + pc, err := c.Group().API().NewPeerConnection(group.IceConfiguration()) if err != nil { return nil, err } @@ -448,9 +449,9 @@ func newUpConn(c client, id string) (*rtpUpConnection, error) { up.mu.Unlock() if complete { - clients := c.Group().getClients(c) + clients := c.Group().GetClients(c) for _, cc := range clients { - cc.pushConn(up.id, up, tracks, up.label) + cc.PushConn(up.id, up, tracks, up.label) } go rtcpUpSender(up) } @@ -750,8 +751,8 @@ func sendUpRTCP(conn *rtpUpConnection) error { rate = r } } - if rate < minBitrate { - rate = minBitrate + if rate < group.MinBitrate { + rate = group.MinBitrate } var ssrcs []uint32 diff --git a/sfu.go b/sfu.go index 5368249..a882aff 100644 --- a/sfu.go +++ b/sfu.go @@ -14,14 +14,14 @@ import ( "runtime" "runtime/pprof" "syscall" + + "sfu/group" ) var httpAddr string var staticRoot string var dataDir string -var groupsDir string var recordingsDir string -var iceFilename string func main() { var cpuprofile, memprofile, mutexprofile string @@ -31,7 +31,7 @@ func main() { "web server root `directory`") flag.StringVar(&dataDir, "data", "./data/", "data `directory`") - flag.StringVar(&groupsDir, "groups", "./groups/", + flag.StringVar(&group.Directory, "groups", "./groups/", "group description `directory`") flag.StringVar(&recordingsDir, "recordings", "./recordings/", "recordings `directory`") @@ -81,9 +81,9 @@ func main() { }() } - iceFilename = filepath.Join(dataDir, "ice-servers.json") + group.IceFilename = filepath.Join(dataDir, "ice-servers.json") - go readPublicGroups() + go group.ReadPublicGroups() webserver() terminate := make(chan os.Signal, 1) diff --git a/stats.go b/stats.go index 7b8719c..aca779a 100644 --- a/stats.go +++ b/stats.go @@ -5,6 +5,7 @@ import ( "sync/atomic" "time" + "sfu/group" "sfu/rtptime" ) @@ -33,15 +34,15 @@ type trackStats struct { } func getGroupStats() []groupStats { - names := getGroupNames() + names := group.GetNames() gs := make([]groupStats, 0, len(names)) for _, name := range names { - g := getGroup(name) + g := group.Get(name) if g == nil { continue } - clients := g.getClients(nil) + clients := g.GetClients(nil) stats := groupStats{ name: name, clients: make([]clientStats, 0, len(clients)), diff --git a/webclient.go b/webclient.go index 5e2d62c..6aeb33c 100644 --- a/webclient.go +++ b/webclient.go @@ -19,56 +19,19 @@ import ( "sfu/conn" "sfu/estimator" + "sfu/group" ) -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 protocolError string - -func (err protocolError) Error() string { - return string(err) -} - -type userError string - -func (err userError) Error() string { - return string(err) -} - func errorToWSCloseMessage(err error) (string, []byte) { var code int var text string switch e := err.(type) { case *websocket.CloseError: code = websocket.CloseNormalClosure - case protocolError: + case group.ProtocolError: code = websocket.CloseProtocolError text = string(e) - case userError: + case group.UserError: code = websocket.CloseNormalClosure text = string(e) default: @@ -84,10 +47,10 @@ func isWSNormalError(err error) bool { } type webClient struct { - group *group + group *group.Group id string - credentials clientCredentials - permissions clientPermissions + credentials group.ClientCredentials + permissions group.ClientPermissions requested map[string]uint32 done chan struct{} writeCh chan interface{} @@ -99,7 +62,7 @@ type webClient struct { up map[string]*rtpUpConnection } -func (c *webClient) Group() *group { +func (c *webClient) Group() *group.Group { return c.group } @@ -107,15 +70,15 @@ func (c *webClient) Id() string { return c.id } -func (c *webClient) Credentials() clientCredentials { +func (c *webClient) Credentials() group.ClientCredentials { return c.credentials } -func (c *webClient) SetPermissions(perms clientPermissions) { +func (c *webClient) SetPermissions(perms group.ClientPermissions) { c.permissions = perms } -func (c *webClient) pushClient(id, username string, add bool) error { +func (c *webClient) PushClient(id, username string, add bool) error { kind := "add" if !add { kind = "delete" @@ -181,7 +144,7 @@ type clientMessage struct { Id string `json:"id,omitempty"` Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` - Permissions clientPermissions `json:"permissions,omitempty"` + Permissions group.ClientPermissions `json:"permissions,omitempty"` Group string `json:"group,omitempty"` Value string `json:"value,omitempty"` Offer *webrtc.SessionDescription `json:"offer,omitempty"` @@ -265,11 +228,11 @@ func delUpConn(c *webClient, id string) bool { delete(c.up, id) c.mu.Unlock() - go func(clients []client) { + go func(clients []group.Client) { for _, c := range clients { - c.pushConn(conn.id, nil, nil, "") + c.PushConn(conn.id, nil, nil, "") } - }(c.Group().getClients(c)) + }(c.Group().GetClients(c)) conn.pc.Close() return true @@ -512,7 +475,7 @@ func gotOffer(c *webClient, id string, offer webrtc.SessionDescription, renegoti func gotAnswer(c *webClient, id string, answer webrtc.SessionDescription) error { down := getDownConn(c, id) if down == nil { - return protocolError("unknown id in answer") + return group.ProtocolError("unknown id in answer") } err := down.pc.SetRemoteDescription(answer) if err != nil { @@ -555,8 +518,8 @@ func (c *webClient) setRequested(requested map[string]uint32) error { return nil } -func pushConns(c client) { - clients := c.Group().getClients(c) +func pushConns(c group.Client) { + clients := c.Group().GetClients(c) for _, cc := range clients { ccc, ok := cc.(*webClient) if ok { @@ -602,7 +565,7 @@ func addDownConnTracks(c *webClient, remote conn.Up, tracks []conn.UpTrack) (*rt return down, nil } -func (c *webClient) pushConn(id string, up conn.Up, tracks []conn.UpTrack, label string) error { +func (c *webClient) PushConn(id string, up conn.Up, tracks []conn.UpTrack, label string) error { err := c.action(pushConnAction{id, up, tracks}) if err != nil { return err @@ -666,7 +629,7 @@ func startClient(conn *websocket.Conn) (err error) { c := &webClient{ id: m.Id, - credentials: clientCredentials{ + credentials: group.ClientCredentials{ m.Username, m.Password, }, @@ -703,24 +666,24 @@ func startClient(conn *websocket.Conn) (err error) { } if m.Type != "join" { - return protocolError("you must join a group first") + return group.ProtocolError("you must join a group first") } - g, err := addClient(m.Group, c) + g, err := group.AddClient(m.Group, c) if err != nil { if os.IsNotExist(err) { - err = userError("group does not exist") + err = group.UserError("group does not exist") } return } if redirect := g.Redirect(); redirect != "" { // We normally redirect at the HTTP level, but the group // description could have been edited in the meantime. - err = userError("group is now at " + redirect) + err = group.UserError("group is now at " + redirect) return } c.group = g - defer delClient(c) + defer group.DelClient(c) return clientLoop(c, conn) } @@ -737,7 +700,7 @@ type addLabelAction struct { } type pushConnsAction struct { - c client + c group.Client } type connectionFailedAction struct { @@ -768,14 +731,14 @@ func clientLoop(c *webClient, ws *websocket.Conn) error { Permissions: c.permissions, }) - h := c.group.getChatHistory() + h := c.group.GetChatHistory() for _, m := range h { err := c.write(clientMessage{ Type: "chat", - Id: m.id, - Username: m.user, - Value: m.value, - Kind: m.kind, + Id: m.Id, + Username: m.User, + Value: m.Value, + Kind: m.Kind, }) if err != nil { return err @@ -853,7 +816,7 @@ func clientLoop(c *webClient, ws *websocket.Conn) error { for i, t := range tracks { ts[i] = t } - go a.c.pushConn(u.id, u, ts, u.label) + go a.c.PushConn(u.id, u, ts, u.label) } case connectionFailedAction: if down := getDownConn(c, a.id); down != nil { @@ -867,7 +830,7 @@ func clientLoop(c *webClient, ws *websocket.Conn) error { for i, t := range down.tracks { tracks[i] = t.remote } - go c.pushConn( + go c.PushConn( down.remote.Id(), down.remote, tracks, down.remote.Label(), ) @@ -899,7 +862,7 @@ func clientLoop(c *webClient, ws *websocket.Conn) error { } } case kickAction: - return userError(a.message) + return group.UserError(a.message) default: log.Printf("unexpected action %T", a) return errors.New("unexpected action") @@ -931,7 +894,7 @@ func failConnection(c *webClient, id string, message string) error { } } if message != "" { - err := c.error(userError(message)) + err := c.error(group.UserError(message)) if err != nil { return err } @@ -939,15 +902,15 @@ func failConnection(c *webClient, id string, message string) error { return nil } -func setPermissions(g *group, id string, perm string) error { - client := g.getClient(id) +func setPermissions(g *group.Group, id string, perm string) error { + client := g.GetClient(id) if client == nil { - return userError("no such user") + return group.UserError("no such user") } c, ok := client.(*webClient) if !ok { - return userError("this is not a real user") + return group.UserError("this is not a real user") } switch perm { @@ -964,7 +927,7 @@ func setPermissions(g *group, id string, perm string) error { case "unpresent": c.permissions.Present = false default: - return userError("unknown permission") + return group.UserError("unknown permission") } return c.action(permissionsChangedAction{}) } @@ -973,18 +936,18 @@ func (c *webClient) kick(message string) error { return c.action(kickAction{message}) } -func kickClient(g *group, id string, message string) error { - client := g.getClient(id) +func kickClient(g *group.Group, id string, message string) error { + client := g.GetClient(id) if client == nil { - return userError("no such user") + return group.UserError("no such user") } - c, ok := client.(kickable) + c, ok := client.(group.Kickable) if !ok { - return userError("this client is not kickable") + return group.UserError("this client is not kickable") } - return c.kick(message) + return c.Kick(message) } func handleClientMessage(c *webClient, m clientMessage) error { @@ -1000,10 +963,10 @@ func handleClientMessage(c *webClient, m clientMessage) error { Type: "abort", Id: m.Id, }) - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } if m.Offer == nil { - return protocolError("null offer") + return group.ProtocolError("null offer") } err := gotOffer( c, m.Id, *m.Offer, m.Kind == "renegotiate", m.Labels, @@ -1014,7 +977,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { } case "answer": if m.Answer == nil { - return protocolError("null answer") + return group.ProtocolError("null answer") } err := gotAnswer(c, m.Id, *m.Answer) if err != nil { @@ -1037,15 +1000,15 @@ func handleClientMessage(c *webClient, m clientMessage) error { } case "ice": if m.Candidate == nil { - return protocolError("null candidate") + return group.ProtocolError("null candidate") } err := gotICE(c, m.Candidate, m.Id) if err != nil { log.Printf("ICE: %v", err) } case "chat": - c.group.addToChatHistory(m.Id, m.Username, m.Kind, m.Value) - clients := c.group.getClients(nil) + c.group.AddToChatHistory(m.Id, m.Username, m.Kind, m.Value) + clients := c.group.GetClients(nil) for _, cc := range clients { cc, ok := cc.(*webClient) if ok { @@ -1055,9 +1018,9 @@ func handleClientMessage(c *webClient, m clientMessage) error { case "groupaction": switch m.Kind { case "clearchat": - c.group.clearChatHistory() + c.group.ClearChatHistory() m := clientMessage{Type: "clearchat"} - clients := c.group.getClients(nil) + clients := c.group.GetClients(nil) for _, cc := range clients { cc, ok := cc.(*webClient) if ok { @@ -1066,21 +1029,21 @@ func handleClientMessage(c *webClient, m clientMessage) error { } case "lock", "unlock": if !c.permissions.Op { - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } c.group.SetLocked(m.Kind == "lock") case "record": if !c.permissions.Record { - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } - for _, cc := range c.group.getClients(c) { + for _, cc := range c.group.GetClients(c) { _, ok := cc.(*diskClient) if ok { - return c.error(userError("already recording")) + return c.error(group.UserError("already recording")) } } disk := NewDiskClient(c.group) - _, err := addClient(c.group.name, disk) + _, err := group.AddClient(c.group.Name(), disk) if err != nil { disk.Close() return c.error(err) @@ -1088,23 +1051,23 @@ func handleClientMessage(c *webClient, m clientMessage) error { go pushConns(disk) case "unrecord": if !c.permissions.Record { - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } - for _, cc := range c.group.getClients(c) { + for _, cc := range c.group.GetClients(c) { disk, ok := cc.(*diskClient) if ok { disk.Close() - delClient(disk) + group.DelClient(disk) } } default: - return protocolError("unknown group action") + return group.ProtocolError("unknown group action") } case "useraction": switch m.Kind { case "op", "unop", "present", "unpresent": if !c.permissions.Op { - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } err := setPermissions(c.group, m.Id, m.Kind) if err != nil { @@ -1112,7 +1075,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { } case "kick": if !c.permissions.Op { - return c.error(userError("not authorised")) + return c.error(group.UserError("not authorised")) } message := m.Value if message == "" { @@ -1123,7 +1086,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { return c.error(err) } default: - return protocolError("unknown user action") + return group.ProtocolError("unknown user action") } case "pong": // nothing @@ -1133,7 +1096,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { }) default: log.Printf("unexpected message: %v", m.Type) - return protocolError("unexpected message") + return group.ProtocolError("unexpected message") } return nil } @@ -1227,7 +1190,7 @@ func (c *webClient) close(data []byte) error { func (c *webClient) error(err error) error { switch e := err.(type) { - case userError: + case group.UserError: return c.write(clientMessage{ Type: "usermessage", Kind: "error", diff --git a/webserver.go b/webserver.go index ac9610b..36710f2 100644 --- a/webserver.go +++ b/webserver.go @@ -18,6 +18,8 @@ import ( "time" "github.com/gorilla/websocket" + + "sfu/group" ) var server *http.Server @@ -47,8 +49,8 @@ func webserver() { IdleTimeout: 120 * time.Second, } server.RegisterOnShutdown(func() { - rangeGroups(func (g *group) bool { - go g.shutdown("server is shutting down") + group.Range(func (g *group.Group) bool { + go g.Shutdown("server is shutting down") return true }) }) @@ -139,7 +141,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) { return } - g, err := addGroup(name, nil) + g, err := group.Add(name, nil) if err != nil { if os.IsNotExist(err) { notFound(w) @@ -168,7 +170,7 @@ func publicHandler(w http.ResponseWriter, r *http.Request) { return } - g := getPublicGroups() + g := group.GetPublic() e := json.NewEncoder(w) e.Encode(g) return @@ -409,8 +411,8 @@ func handleGroupAction(w http.ResponseWriter, r *http.Request, group string) { } } -func checkGroupPermissions(w http.ResponseWriter, r *http.Request, group string) bool { - desc, err := getDescription(group) +func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname string) bool { + desc, err := group.GetDescription(groupname) if err != nil { return false } @@ -420,7 +422,7 @@ func checkGroupPermissions(w http.ResponseWriter, r *http.Request, group string) return false } - p, err := getPermission(desc, clientCredentials{user, pass}) + p, err := desc.GetPermission(group.ClientCredentials{user, pass}) if err != nil || !p.Record { return false }