25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

378 satır
9.7 KiB

  1. package httpserver
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/AFASystems/presence/internal/pkg/model"
  11. "github.com/AFASystems/presence/internal/pkg/utils"
  12. "github.com/gorilla/handlers"
  13. "github.com/gorilla/mux"
  14. "github.com/gorilla/websocket"
  15. )
  16. var (
  17. upgrader = websocket.Upgrader{
  18. ReadBufferSize: 1024,
  19. WriteBufferSize: 1024,
  20. CheckOrigin: func(r *http.Request) bool {
  21. return true
  22. },
  23. }
  24. )
  25. const (
  26. writeWait = 10 * time.Second
  27. pongWait = 60 * time.Second
  28. pingPeriod = (pongWait * 9) / 10
  29. beaconPeriod = 2 * time.Second
  30. )
  31. // Init store in main or anywhere else and pass it to all initializer functions
  32. // called in main, then with controllers or handlers use wrapper that takes entire store
  33. // allocates only the properties that need to be passed into the controller
  34. func StartHTTPServer(addr string, ctx *model.AppContext) {
  35. headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
  36. originsOk := handlers.AllowedOrigins([]string{"*"})
  37. methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"})
  38. // Set up HTTP server
  39. r := mux.NewRouter()
  40. r.HandleFunc("/api/results", resultsHandler(&ctx.HTTPResultLock, &ctx.HTTPResults))
  41. r.HandleFunc("/api/beacons/{beacon_id}", BeaconsDeleteHandler(&ctx.Beacons, ctx.ButtonsList)).Methods("DELETE")
  42. r.HandleFunc("/api/beacons", BeaconsListHandler(&ctx.Beacons)).Methods("GET")
  43. r.HandleFunc("/api/beacons", BeaconsAddHandler(&ctx.Beacons)).Methods("POST") //since beacons are hashmap, just have put and post be same thing. it'll either add or modify that entry
  44. r.HandleFunc("/api/beacons", BeaconsAddHandler(&ctx.Beacons)).Methods("PUT")
  45. r.HandleFunc("/api/latest-beacons", latestBeaconsListHandler(&ctx.LatestListLock, ctx.LatestBeaconsList)).Methods("GET")
  46. r.HandleFunc("/api/settings", settingsListHandler(&ctx.Settings)).Methods("GET")
  47. r.HandleFunc("/api/settings", settingsEditHandler(&ctx.Settings)).Methods("POST")
  48. r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("static_html/js/"))))
  49. r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("static_html/css/"))))
  50. r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("static_html/img/"))))
  51. r.PathPrefix("/").Handler(http.FileServer(http.Dir("static_html/")))
  52. http.Handle("/", r)
  53. mxWS := mux.NewRouter()
  54. mxWS.HandleFunc("/ws/api/beacons", serveWs(&ctx.HTTPResultLock, &ctx.HTTPResults))
  55. mxWS.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs(&ctx.LatestListLock, ctx.LatestBeaconsList))
  56. mxWS.HandleFunc("/ws/broadcast", handleConnections(ctx.Clients, &ctx.Broadcast))
  57. http.Handle("/ws/", mxWS)
  58. go handleMessages(ctx.Clients, &ctx.Broadcast)
  59. go func() {
  60. log.Fatal(http.ListenAndServe(*model.HTTPWSHostPathPtr, nil))
  61. }()
  62. http.ListenAndServe(*model.HTTPHostPathPtr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
  63. }
  64. // TODO: rather add defer to unlock the files
  65. func resultsHandler(lock *sync.RWMutex, httpResults *model.HTTPLocationsList) http.HandlerFunc {
  66. return func(w http.ResponseWriter, r *http.Request) {
  67. lock.Lock()
  68. js, err := json.Marshal(httpResults)
  69. lock.Unlock()
  70. if err != nil {
  71. http.Error(w, err.Error(), http.StatusInternalServerError)
  72. return
  73. }
  74. w.Write(js)
  75. }
  76. }
  77. func BeaconsListHandler(beacons *model.BeaconsList) http.HandlerFunc {
  78. return func(w http.ResponseWriter, r *http.Request) {
  79. beacons.Lock.RLock()
  80. js, err := json.Marshal(beacons.Beacons)
  81. beacons.Lock.RUnlock()
  82. if err != nil {
  83. http.Error(w, err.Error(), http.StatusInternalServerError)
  84. return
  85. }
  86. w.Write(js)
  87. }
  88. }
  89. func BeaconsAddHandler(beacons *model.BeaconsList) http.HandlerFunc {
  90. return func(w http.ResponseWriter, r *http.Request) {
  91. decoder := json.NewDecoder(r.Body)
  92. var inBeacon model.Beacon
  93. err := decoder.Decode(&inBeacon)
  94. if err != nil {
  95. http.Error(w, err.Error(), 400)
  96. return
  97. }
  98. if (len(strings.TrimSpace(inBeacon.Name)) == 0) || (len(strings.TrimSpace(inBeacon.Beacon_id)) == 0) {
  99. http.Error(w, "name and beacon_id cannot be blank", 400)
  100. return
  101. }
  102. beacons.Beacons[inBeacon.Beacon_id] = inBeacon
  103. err = utils.PersistBeacons(beacons)
  104. if err != nil {
  105. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  106. return
  107. }
  108. w.Write([]byte("ok"))
  109. }
  110. }
  111. func BeaconsDeleteHandler(beacons *model.BeaconsList, buttonsList map[string]model.Button) http.HandlerFunc {
  112. return func(w http.ResponseWriter, r *http.Request) {
  113. vars := mux.Vars(r)
  114. fmt.Println("route param: ", vars)
  115. beaconId := vars["beacon_id"]
  116. _, ok := beacons.Beacons[beaconId]
  117. if !ok {
  118. http.Error(w, "no beacon with the specified id", 400) // change the status code
  119. return
  120. }
  121. delete(beacons.Beacons, beaconId)
  122. _, ok = buttonsList[beaconId]
  123. if ok {
  124. delete(buttonsList, beaconId)
  125. }
  126. err := utils.PersistBeacons(beacons)
  127. if err != nil {
  128. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  129. return
  130. }
  131. w.Write([]byte("ok"))
  132. }
  133. }
  134. func latestBeaconsListHandler(lock *sync.RWMutex, latestBeaconsList map[string]model.Beacon) http.HandlerFunc {
  135. return func(w http.ResponseWriter, r *http.Request) {
  136. lock.RLock()
  137. var la = make([]model.Beacon, 0)
  138. for _, b := range latestBeaconsList {
  139. la = append(la, b)
  140. }
  141. lock.RUnlock()
  142. js, err := json.Marshal(la)
  143. if err != nil {
  144. http.Error(w, err.Error(), http.StatusInternalServerError)
  145. return
  146. }
  147. w.Write(js)
  148. }
  149. }
  150. func settingsListHandler(settings *model.Settings) http.HandlerFunc {
  151. return func(w http.ResponseWriter, r *http.Request) {
  152. js, err := json.Marshal(settings)
  153. if err != nil {
  154. http.Error(w, err.Error(), http.StatusInternalServerError)
  155. return
  156. }
  157. w.Write(js)
  158. }
  159. }
  160. func settingsEditHandler(settings *model.Settings) http.HandlerFunc {
  161. return func(w http.ResponseWriter, r *http.Request) {
  162. decoder := json.NewDecoder(r.Body)
  163. var inSettings model.Settings
  164. err := decoder.Decode(&inSettings)
  165. if err != nil {
  166. http.Error(w, err.Error(), 400)
  167. return
  168. }
  169. //make sure values are > 0
  170. if (inSettings.Location_confidence <= 0) ||
  171. (inSettings.Last_seen_threshold <= 0) ||
  172. (inSettings.HA_send_interval <= 0) {
  173. http.Error(w, "values must be greater than 0", 400)
  174. return
  175. }
  176. *settings = inSettings
  177. err = utils.PersistSettings(settings)
  178. if err != nil {
  179. http.Error(w, "trouble persisting settings, create bucket", 500)
  180. return
  181. }
  182. w.Write([]byte("ok"))
  183. }
  184. }
  185. func reader(ws *websocket.Conn) {
  186. defer ws.Close()
  187. ws.SetReadLimit(512)
  188. ws.SetReadDeadline(time.Now().Add(pongWait))
  189. ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  190. for {
  191. _, _, err := ws.ReadMessage()
  192. if err != nil {
  193. break
  194. }
  195. }
  196. }
  197. func writer(ws *websocket.Conn, lock *sync.RWMutex, httpResults *model.HTTPLocationsList) {
  198. pingTicker := time.NewTicker(pingPeriod)
  199. beaconTicker := time.NewTicker(beaconPeriod)
  200. defer func() {
  201. pingTicker.Stop()
  202. beaconTicker.Stop()
  203. ws.Close()
  204. }()
  205. for {
  206. select {
  207. case <-beaconTicker.C:
  208. lock.RLock()
  209. js, err := json.Marshal(httpResults)
  210. lock.RUnlock()
  211. if err != nil {
  212. js = []byte("error")
  213. }
  214. ws.SetWriteDeadline(time.Now().Add(writeWait))
  215. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  216. return
  217. }
  218. case <-pingTicker.C:
  219. ws.SetWriteDeadline(time.Now().Add(writeWait))
  220. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  221. return
  222. }
  223. }
  224. }
  225. }
  226. func serveWs(lock *sync.RWMutex, httpResults *model.HTTPLocationsList) http.HandlerFunc {
  227. return func(w http.ResponseWriter, r *http.Request) {
  228. ws, err := upgrader.Upgrade(w, r, nil)
  229. if err != nil {
  230. if _, ok := err.(websocket.HandshakeError); !ok {
  231. log.Println(err)
  232. }
  233. return
  234. }
  235. go writer(ws, lock, httpResults)
  236. reader(ws)
  237. }
  238. }
  239. func latestBeaconWriter(ws *websocket.Conn, latestBeaconsList map[string]model.Beacon, lock *sync.RWMutex) {
  240. pingTicker := time.NewTicker(pingPeriod)
  241. beaconTicker := time.NewTicker(beaconPeriod)
  242. defer func() {
  243. pingTicker.Stop()
  244. beaconTicker.Stop()
  245. ws.Close()
  246. }()
  247. for {
  248. select {
  249. case <-beaconTicker.C:
  250. lock.RLock()
  251. var la = make([]model.Beacon, 0)
  252. for _, b := range latestBeaconsList {
  253. la = append(la, b)
  254. }
  255. lock.RUnlock()
  256. js, err := json.Marshal(la)
  257. if err != nil {
  258. js = []byte("error")
  259. }
  260. ws.SetWriteDeadline(time.Now().Add(writeWait))
  261. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  262. return
  263. }
  264. case <-pingTicker.C:
  265. ws.SetWriteDeadline(time.Now().Add(writeWait))
  266. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  267. return
  268. }
  269. }
  270. }
  271. }
  272. func serveLatestBeaconsWs(lock *sync.RWMutex, latestBeaconsList map[string]model.Beacon) http.HandlerFunc {
  273. return func(w http.ResponseWriter, r *http.Request) {
  274. ws, err := upgrader.Upgrade(w, r, nil)
  275. if err != nil {
  276. if _, ok := err.(websocket.HandshakeError); !ok {
  277. log.Println(err)
  278. }
  279. return
  280. }
  281. go latestBeaconWriter(ws, latestBeaconsList, lock)
  282. reader(ws)
  283. }
  284. }
  285. func handleConnections(clients map[*websocket.Conn]bool, broadcast *chan model.Message) http.HandlerFunc {
  286. return func(w http.ResponseWriter, r *http.Request) {
  287. ws, err := upgrader.Upgrade(w, r, nil)
  288. if err != nil {
  289. log.Fatal(err)
  290. }
  291. defer ws.Close()
  292. clients[ws] = true
  293. for {
  294. var msg model.Message
  295. err := ws.ReadJSON(&msg)
  296. if err != nil {
  297. log.Printf("error: %v", err)
  298. delete(clients, ws)
  299. break
  300. }
  301. *broadcast <- msg
  302. }
  303. }
  304. }
  305. func handleMessages(clients map[*websocket.Conn]bool, broadcast *chan model.Message) {
  306. for {
  307. msg := <-*broadcast
  308. for client := range clients {
  309. err := client.WriteJSON(msg)
  310. if err != nil {
  311. log.Printf("error: %v", err)
  312. client.Close()
  313. delete(clients, client)
  314. }
  315. }
  316. }
  317. }