1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "net/http" 7 "strconv" 8 "sync" 9 10 "golang.org/x/net/websocket" 11 "vimagination.zapto.org/jsonrpc" 12 ) 13 14 var ( 15 mu sync.RWMutex 16 conns = make(map[*conn]struct{}) 17 names = make(map[string]*conn) 18 users = json.RawMessage{'{', '"', 'i', 'd', '"', ':', '-', '1', ',', '"', 'r', 'e', 's', 'u', 'l', 't', '"', ':', '[', ']', '}'} 19 ) 20 21 type conn struct { 22 Name string 23 Requests map[string]struct{} 24 rpc *jsonrpc.Server 25 } 26 27 type nameSDP struct { 28 Name string `json:"name"` 29 SDP json.RawMessage `json:"sdp"` 30 } 31 32 func (c *conn) HandleRPC(method string, data json.RawMessage) (interface{}, error) { 33 mu.RLock() 34 name := c.Name 35 mu.RUnlock() 36 if name == "" { 37 switch method { 38 case "init": 39 var name string 40 if err := json.Unmarshal(data, &name); err != nil { 41 return nil, err 42 } 43 if name == "" { 44 return nil, ErrInvalidName 45 } 46 mu.Lock() 47 defer mu.Unlock() 48 if _, ok := names[name]; ok { 49 return nil, ErrNameTaken 50 } 51 users = users[:len(users)-2] 52 if len(users) > 20 { 53 users = append(users, ',') 54 } 55 c.Name = name 56 names[name] = c 57 Broadcast(c, BroadcastUserAdd, data) 58 users = append(users, data...) 59 users = append(users, ']', '}') 60 return nil, nil 61 } 62 } else { 63 switch method { 64 case "request": 65 var nameSDP nameSDP 66 if err := json.Unmarshal(data, &nameSDP); err != nil { 67 return nil, err 68 } 69 if nameSDP.Name == "" { 70 return nil, ErrInvalidName 71 } 72 mu.Lock() 73 defer mu.Unlock() 74 if _, ok := c.Requests[name]; ok { 75 return nil, ErrAlreadyRequested 76 } 77 oc, ok := names[nameSDP.Name] 78 if !ok { 79 return nil, ErrNameNotFound 80 } 81 data = append(data[:0], "{\"name\":"...) 82 data = json.RawMessage(strconv.AppendQuote(data, name)) 83 data = append(data, ",\"sdp\":"...) 84 data = append(data, nameSDP.SDP...) 85 data = append(data, '}') 86 oc.rpc.SendData(buildBroadcast(BroadcastSDP, data)) 87 c.Requests[name] = struct{}{} 88 return nil, nil 89 case "cancel": 90 var name string 91 if err := json.Unmarshal(data, &name); err != nil { 92 return nil, err 93 } 94 if name == "" { 95 return nil, ErrInvalidName 96 } 97 mu.Lock() 98 defer mu.Unlock() 99 if _, ok := c.Requests[c.Name]; ok { 100 return nil, ErrNoRequest 101 } 102 oc, ok := names[name] 103 if !ok { 104 return nil, ErrNameNotFound 105 } 106 delete(c.Requests, name) 107 data = strconv.AppendQuote(data[:0], c.Name) 108 oc.rpc.SendData(buildBroadcast(BroadcastCancel, data)) 109 return nil, nil 110 case "accept": 111 var nameSDP nameSDP 112 if err := json.Unmarshal(data, &nameSDP); err != nil { 113 return nil, err 114 } 115 if nameSDP.Name == "" { 116 return nil, ErrInvalidName 117 } 118 mu.Lock() 119 defer mu.Unlock() 120 oc, ok := names[name] 121 if !ok { 122 return nil, ErrNameNotFound 123 } 124 if _, ok := oc.Requests[c.Name]; ok { 125 return nil, ErrNoRequest 126 } 127 delete(oc.Requests, c.Name) 128 data = append(data[:0], "{\"name\":"...) 129 data = json.RawMessage(strconv.AppendQuote(data, name)) 130 data = append(data, ",\"sdp\":"...) 131 data = append(data, nameSDP.SDP...) 132 data = append(data, '}') 133 oc.rpc.SendData(buildBroadcast(BroadcastDecline, data)) 134 return nil, nil 135 case "decline": 136 var name string 137 if err := json.Unmarshal(data, &name); err != nil { 138 return nil, err 139 } 140 if name == "" { 141 return nil, ErrInvalidName 142 } 143 mu.Lock() 144 defer mu.Unlock() 145 oc, ok := names[name] 146 if !ok { 147 return nil, ErrNameNotFound 148 } 149 if _, ok := oc.Requests[c.Name]; ok { 150 return nil, ErrNoRequest 151 } 152 delete(oc.Requests, c.Name) 153 oc.rpc.SendData(buildBroadcast(BroadcastDecline, data)) 154 return nil, nil 155 } 156 } 157 return nil, ErrUnknownEndpoint 158 } 159 160 func wsHandler(wconn *websocket.Conn) { 161 var c conn 162 mu.Lock() 163 conns[&c] = struct{}{} 164 mu.Unlock() 165 c = conn{ 166 Requests: make(map[string]struct{}), 167 rpc: jsonrpc.New(wconn, &c), 168 } 169 c.rpc.SendData(users) 170 c.rpc.Handle() 171 mu.Lock() 172 if c.Name != "" { 173 delete(names, c.Name) 174 Broadcast(&c, BroadcastUserRemove, json.RawMessage(strconv.Quote(c.Name))) 175 users = users[:19] 176 for name := range names { 177 if len(names) > 20 { 178 users = append(users, ',') 179 } 180 users = strconv.AppendQuote(users, name) 181 } 182 users = append(users, ']', '}') 183 } 184 delete(conns, &c) 185 mu.Unlock() 186 } 187 188 func init() { 189 http.Handle("/socket", websocket.Handler(wsHandler)) 190 } 191 192 var ( 193 ErrNameTaken = errors.New("name taken") 194 ErrInvalidName = errors.New("invalid name") 195 ErrNameNotFound = errors.New("name not found") 196 ErrAlreadyRequested = errors.New("already requested") 197 ErrNoRequest = errors.New("no request") 198 ErrUnknownEndpoint = errors.New("unknown endpoint") 199 ) 200