hub.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //Modified by ArOZ Online Project for websocket message redirection purpose
  5. package main
  6. import (
  7. "log"
  8. "net/http"
  9. "encoding/json"
  10. uuid "github.com/google/uuid"
  11. "strings"
  12. )
  13. // Hub maintains the set of active clients and broadcasts messages to the
  14. // clients.
  15. type Hub struct {
  16. // Registered clients.
  17. clients map[*Client]bool
  18. // Inbound messages from the clients.
  19. broadcast chan msgpackage
  20. // Register requests from the clients.
  21. register chan *Client
  22. // Unregister requests from clients.
  23. unregister chan *Client
  24. //Register the username from clients
  25. usernames map[*Client]string
  26. //State if the user has logged in
  27. loggedin map[*Client]bool
  28. //Module name for application registry
  29. channel map[*Client] string
  30. //Instance UUID for direct messaging between two clients
  31. uuids map[*Client] string
  32. }
  33. func newHub() *Hub {
  34. return &Hub{
  35. broadcast: make(chan msgpackage),
  36. register: make(chan *Client),
  37. unregister: make(chan *Client),
  38. clients: make(map[*Client]bool),
  39. usernames: make(map[*Client]string),
  40. loggedin: make(map[*Client]bool),
  41. channel: make(map[*Client] string),
  42. uuids: make(map[*Client] string),
  43. }
  44. }
  45. type validAuthJSON struct {
  46. username string
  47. signDevice string
  48. createdTime int
  49. expTime int
  50. discarded bool
  51. }
  52. func checkLogin(h *Hub, sender *Client) bool{
  53. if (h.loggedin[sender] == true){
  54. return true
  55. }
  56. return false
  57. }
  58. func sendResp(h *Hub, reciver *Client, message []byte){
  59. select {
  60. case reciver.send <- msgpackage{message,reciver}:
  61. default:
  62. close(reciver.send)
  63. delete(h.clients, reciver)
  64. }
  65. }
  66. func sendBadReq(h *Hub, reciver *Client, command string){
  67. message := `{"type":"resp","command":"` + command + `", "data":"400 Bad Request"}`
  68. select {
  69. case reciver.send <- msgpackage{[]byte(message),reciver}:
  70. default:
  71. close(reciver.send)
  72. delete(h.clients, reciver)
  73. }
  74. }
  75. func runCommand(h *Hub, sender *Client, message string) string{
  76. commandChunks := strings.Split(message[1:]," ")
  77. commandOutput := `{"type":"resp","command":"` + commandChunks[0] + `", "data":"405 Method Not Allowed"}`;
  78. //log.Println(commandChunks)
  79. switch commandChunks[0]{
  80. case "login":
  81. if (len(commandChunks) != 3){
  82. //Malformated command input.
  83. sendBadReq(h, sender, "login");
  84. log.Println("Bad command received.")
  85. return "";
  86. }
  87. registerModuleName := commandChunks[1]
  88. thisJWT := commandChunks[2]
  89. validationURL := string(*endpt) + "?token=" + string(thisJWT)
  90. //Get response from server for authentication check
  91. resp, err := http.Get(validationURL)
  92. if err != nil {
  93. //Cannot reach auth services.
  94. commandOutput = `{"type":"resp","command":"login", "data":"503 Service Unavailable"}`;
  95. sendResp(h, sender, []byte(commandOutput))
  96. log.Println("Failed to access auth service. Is the jwt validation endpoint correct?")
  97. return "";
  98. }
  99. defer resp.Body.Close()
  100. //log.Printf("%#v\n", resp)
  101. dec := json.NewDecoder(resp.Body)
  102. if dec == nil {
  103. //JSON parse error
  104. commandOutput = `{"type":"resp","command":"login", "data":"500 Internal Server Error"}`;
  105. sendResp(h, sender, []byte(commandOutput))
  106. log.Println("Failed parse JSON return value. Is the aobws version match the ArOZ Online System version?")
  107. return "";
  108. //panic("Failed to start decoding JSON data")
  109. }
  110. json_map := make(map[string]interface{})
  111. err = dec.Decode(&json_map)
  112. if err != nil {
  113. panic(err)
  114. }
  115. if (json_map["error"] == nil){
  116. //Check if token discarded. If yes, also report as failed to login.
  117. if (json_map["discarded"] == true){
  118. //This token is discarded. Ignore login
  119. h.loggedin[sender] = false
  120. commandOutput = `{"type":"resp","command":"login", "data":"401 Unauthorized"}`;
  121. sendResp(h, sender, []byte(commandOutput))
  122. return "";
  123. }else{
  124. //This authentication is successful
  125. h.loggedin[sender] = true
  126. h.channel[sender] = registerModuleName
  127. h.usernames[sender] = json_map["username"].(string)
  128. commandOutput = `{"type":"resp","command":"login", "data":"202 Accepted"}`;
  129. }
  130. }else{
  131. //This authentication failed
  132. h.loggedin[sender] = false
  133. commandOutput = `{"type":"resp","command":"login", "data":"401 Unauthorized"}`;
  134. sendResp(h, sender, []byte(commandOutput))
  135. return "";
  136. }
  137. case "chklogin":
  138. if (len(commandChunks) != 1){
  139. //Malformated command input.
  140. sendBadReq(h, sender, "chklogin");
  141. log.Println("Bad command received.")
  142. return "";
  143. }
  144. if (checkLogin(h, sender)){
  145. commandOutput = `{"type":"resp","command":"chklogin", "data":"` + "Logged in as " + h.usernames[sender] + `"}`;
  146. }else{
  147. sendResp(h, sender, []byte(`{"type":"resp","command":"chklogin", "data":"Not logged in"}`))
  148. return ""
  149. }
  150. case "logout":
  151. if (len(commandChunks) != 1){
  152. //Malformated command input.
  153. sendBadReq(h, sender, "logout");
  154. log.Println("Bad command received.")
  155. return "";
  156. }
  157. if (checkLogin(h, sender)){
  158. h.loggedin[sender] = false
  159. sendResp(h, sender, []byte(`{"type":"resp","command":"logout", "data":"202 Accepted"}`))
  160. return ""
  161. }else{
  162. commandOutput = `{"type":"resp","command":"logout", "data":"401 Unauthorized"}`;
  163. }
  164. case "chkchannel":
  165. if (len(commandChunks) != 1){
  166. //Malformated command input.
  167. sendBadReq(h, sender, "chkchannel");
  168. log.Println("Bad command received.")
  169. return "";
  170. }
  171. if (checkLogin(h, sender)){
  172. commandOutput = `{"type":"resp","command":"chkchannel", "data":"` + h.channel[sender] + `"}`;
  173. }else{
  174. commandOutput = `{"type":"resp","command":"chkchannel", "data":"401 Unauthorized"}`;
  175. }
  176. case "chkuuid":
  177. if (len(commandChunks) != 1){
  178. //Malformated command input.
  179. sendBadReq(h, sender, "chkuuid");
  180. log.Println("Bad command received.")
  181. return "";
  182. }
  183. if (checkLogin(h, sender)){
  184. commandOutput = `{"type":"resp","command":"chkuuid", "data":"` + h.uuids[sender] + `"}`;
  185. }else{
  186. commandOutput = `{"type":"resp","command":"chkuuid", "data":"401 Unauthorized"}`;
  187. }
  188. case "tell":
  189. //Tell comamnd, given another username
  190. targetUsername := commandChunks[1]
  191. messageToBeDelivered := strings.Join(commandChunks[2:]," ")
  192. //Parse the common communication protocol
  193. msgpack := "{\"type\": \"tell\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" + messageToBeDelivered + "\"}"
  194. if (checkLogin(h, sender)){
  195. //Send the message to all the clients with given username
  196. for client := range h.clients {
  197. if h.usernames[client] == targetUsername && client != sender{
  198. sendResp(h, client, []byte(msgpack))
  199. }
  200. }
  201. commandOutput = `{"type":"resp","command":"tell", "data":"200 OK"}`;
  202. }else{
  203. commandOutput = `{"type":"resp","command":"tell", "data":"401 Unauthorized"}`;
  204. }
  205. case "utell":
  206. //Tell comamnd, but using UUID instead of username
  207. targetUUID := commandChunks[1]
  208. messageToBeDelivered := strings.Join(commandChunks[2:]," ")
  209. msgpack := "{\"type\": \"utell\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" + messageToBeDelivered + "\"}"
  210. if (checkLogin(h, sender)){
  211. //Send the message to all the clients with given username
  212. for client := range h.clients {
  213. if h.uuids[client] == targetUUID{
  214. sendResp(h, client, []byte(msgpack))
  215. }
  216. }
  217. commandOutput = `{"type":"resp","command":"utell", "data":"200 OK"}`;
  218. }else{
  219. commandOutput = `{"type":"resp","command":"utell", "data":"401 Unauthorized"}`;
  220. }
  221. case "help":
  222. //The standard help command. Shows the list of command usable via this websocket reflector
  223. resp := `ArOZ Online Base System WebSocket Reflector
  224. Usage:
  225. /login {channel} {shadowJWT token}
  226. /chklogin
  227. /logout
  228. /chkchannel
  229. /checkuuid
  230. /tell {username} {message}
  231. /utell {connnection UUID} {message}`;
  232. sendResp(h, sender, []byte(resp))
  233. return ""
  234. }
  235. return commandOutput
  236. }
  237. func handleMessage(h *Hub, sender *Client, message []byte) (bool, []byte){
  238. if (string(message)[0:1] == "/"){
  239. //This is a command.
  240. returnString := runCommand(h, sender, string(message))
  241. return false,[]byte(returnString)
  242. }
  243. return true,message
  244. }
  245. func (h *Hub) run() {
  246. for {
  247. select {
  248. case client := <-h.register:
  249. h.clients[client] = true //Enable this client as registered user
  250. h.usernames[client] = "anonymous" //Set its name to anonymous before it login
  251. h.loggedin[client] = false //Set to not logged in
  252. h.uuids[client] = (uuid.Must(uuid.NewRandom())).String() //Given this connection an uuid
  253. case client := <-h.unregister:
  254. if _, ok := h.clients[client]; ok {
  255. delete(h.clients, client)
  256. close(client.send)
  257. }
  258. case msg := <-h.broadcast:
  259. message := msg.message
  260. sender := msg.clientID
  261. log.Println(string(message), h.usernames[sender])
  262. //Handle the message
  263. brodcaseMessage, message := handleMessage(h,sender, message)
  264. if (string(message) == ""){
  265. //Do not return anything
  266. break;
  267. }
  268. //Check if the sender has already logged in
  269. if (h.loggedin[sender] == true){
  270. //User logged in
  271. if brodcaseMessage{
  272. //Broadcast the message to the Clients in the same channel
  273. senderChannel := h.channel[sender]
  274. msgpack := "{\"type\": \"broadcast\",\"sender\": \"" + h.usernames[sender] + "\", \"connUUID\": \"" + h.uuids[sender] + "\", \"data\": \"" + string(message) + "\"}"
  275. for client := range h.clients {
  276. //Send the message to all clients in the same channel
  277. if h.channel[client] == senderChannel{
  278. select {
  279. case client.send <- msgpackage{[]byte(msgpack),client}:
  280. default:
  281. close(client.send)
  282. delete(h.clients, client)
  283. }
  284. }
  285. }
  286. }else{
  287. //Only send reply back to the user
  288. select {
  289. case sender.send <- msgpackage{message,sender}:
  290. default:
  291. close(sender.send)
  292. delete(h.clients, sender)
  293. }
  294. }
  295. }else{
  296. //User not logged in
  297. select {
  298. case sender.send <- msgpackage{[]byte(`{"type":"resp","command":"generic", "data":"401 Unauthorized"}`),sender}:
  299. default:
  300. close(sender.send)
  301. delete(h.clients, sender)
  302. }
  303. }
  304. }
  305. }
  306. }