Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 

372 wiersze
9.5 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/persistence"
  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.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.LatestList)).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.HTTPResults))
  55. mxWS.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs(&ctx.LatestList))
  56. mxWS.HandleFunc("/ws/broadcast", handleConnections(ctx.Clients, &ctx.Broadcast))
  57. http.Handle("/ws/", mxWS)
  58. go handleMessages(ctx.Clients, &ctx.Broadcast)
  59. http.ListenAndServe(addr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
  60. }
  61. func resultsHandler(httpResults *model.HTTPResultsList) http.HandlerFunc {
  62. return func(w http.ResponseWriter, r *http.Request) {
  63. httpResults.HTTPResultsLock.Lock()
  64. defer httpResults.HTTPResultsLock.Unlock()
  65. js, err := json.Marshal(httpResults.HTTPResults)
  66. if err != nil {
  67. http.Error(w, err.Error(), http.StatusInternalServerError)
  68. return
  69. }
  70. w.Write(js)
  71. }
  72. }
  73. func BeaconsListHandler(beacons *model.BeaconsList) http.HandlerFunc {
  74. return func(w http.ResponseWriter, r *http.Request) {
  75. beacons.Lock.RLock()
  76. js, err := json.Marshal(beacons.Beacons)
  77. beacons.Lock.RUnlock()
  78. if err != nil {
  79. http.Error(w, err.Error(), http.StatusInternalServerError)
  80. return
  81. }
  82. w.Write(js)
  83. }
  84. }
  85. func BeaconsAddHandler(beacons *model.BeaconsList) http.HandlerFunc {
  86. return func(w http.ResponseWriter, r *http.Request) {
  87. decoder := json.NewDecoder(r.Body)
  88. var inBeacon model.Beacon
  89. err := decoder.Decode(&inBeacon)
  90. if err != nil {
  91. http.Error(w, err.Error(), 400)
  92. return
  93. }
  94. if (len(strings.TrimSpace(inBeacon.Name)) == 0) || (len(strings.TrimSpace(inBeacon.Beacon_id)) == 0) {
  95. http.Error(w, "name and beacon_id cannot be blank", 400)
  96. return
  97. }
  98. beacons.Beacons[inBeacon.Beacon_id] = inBeacon
  99. err = persistence.PersistBeacons(beacons)
  100. if err != nil {
  101. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  102. return
  103. }
  104. w.Write([]byte("ok"))
  105. }
  106. }
  107. func BeaconsDeleteHandler(beacons *model.BeaconsList, buttonsList map[string]model.Button) http.HandlerFunc {
  108. return func(w http.ResponseWriter, r *http.Request) {
  109. vars := mux.Vars(r)
  110. fmt.Println("route param: ", vars)
  111. beaconId := vars["beacon_id"]
  112. _, ok := beacons.Beacons[beaconId]
  113. if !ok {
  114. http.Error(w, "no beacon with the specified id", 400) // change the status code
  115. return
  116. }
  117. delete(beacons.Beacons, beaconId)
  118. _, ok = buttonsList[beaconId]
  119. if ok {
  120. delete(buttonsList, beaconId)
  121. }
  122. err := persistence.PersistBeacons(beacons)
  123. if err != nil {
  124. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  125. return
  126. }
  127. w.Write([]byte("ok"))
  128. }
  129. }
  130. func latestBeaconsListHandler(latestList *model.LatestBeaconsList) http.HandlerFunc {
  131. return func(w http.ResponseWriter, r *http.Request) {
  132. latestList.LatestListLock.RLock()
  133. var la = make([]model.Beacon, 0)
  134. for _, b := range latestList.LatestList {
  135. la = append(la, b)
  136. }
  137. latestList.LatestListLock.RUnlock()
  138. js, err := json.Marshal(la)
  139. if err != nil {
  140. http.Error(w, err.Error(), http.StatusInternalServerError)
  141. return
  142. }
  143. w.Write(js)
  144. }
  145. }
  146. func SettingsListHandler(settings *model.Settings) http.HandlerFunc {
  147. return func(w http.ResponseWriter, r *http.Request) {
  148. js, err := json.Marshal(settings)
  149. if err != nil {
  150. http.Error(w, err.Error(), http.StatusInternalServerError)
  151. return
  152. }
  153. w.Write(js)
  154. }
  155. }
  156. func SettingsEditHandler(settings *model.Settings) http.HandlerFunc {
  157. return func(w http.ResponseWriter, r *http.Request) {
  158. decoder := json.NewDecoder(r.Body)
  159. var inSettings model.Settings
  160. err := decoder.Decode(&inSettings)
  161. if err != nil {
  162. http.Error(w, err.Error(), 400)
  163. return
  164. }
  165. //make sure values are > 0
  166. if (inSettings.Location_confidence <= 0) ||
  167. (inSettings.Last_seen_threshold <= 0) ||
  168. (inSettings.HA_send_interval <= 0) {
  169. http.Error(w, "values must be greater than 0", 400)
  170. return
  171. }
  172. *settings = inSettings
  173. err = persistence.PersistSettings(settings)
  174. if err != nil {
  175. http.Error(w, "trouble persisting settings, create bucket", 500)
  176. return
  177. }
  178. w.Write([]byte("ok"))
  179. }
  180. }
  181. func reader(ws *websocket.Conn) {
  182. defer ws.Close()
  183. ws.SetReadLimit(512)
  184. ws.SetReadDeadline(time.Now().Add(pongWait))
  185. ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  186. for {
  187. _, _, err := ws.ReadMessage()
  188. if err != nil {
  189. break
  190. }
  191. }
  192. }
  193. func writer(ws *websocket.Conn, httpResult *model.HTTPResultsList) {
  194. pingTicker := time.NewTicker(pingPeriod)
  195. beaconTicker := time.NewTicker(beaconPeriod)
  196. defer func() {
  197. pingTicker.Stop()
  198. beaconTicker.Stop()
  199. ws.Close()
  200. }()
  201. for {
  202. select {
  203. case <-beaconTicker.C:
  204. httpResult.HTTPResultsLock.Lock()
  205. defer httpResult.HTTPResultsLock.Unlock()
  206. js, err := json.Marshal(httpResult.HTTPResults)
  207. if err != nil {
  208. js = []byte("error")
  209. }
  210. ws.SetWriteDeadline(time.Now().Add(writeWait))
  211. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  212. return
  213. }
  214. case <-pingTicker.C:
  215. ws.SetWriteDeadline(time.Now().Add(writeWait))
  216. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  217. return
  218. }
  219. }
  220. }
  221. }
  222. func serveWs(httpResult *model.HTTPResultsList) http.HandlerFunc {
  223. return func(w http.ResponseWriter, r *http.Request) {
  224. ws, err := upgrader.Upgrade(w, r, nil)
  225. if err != nil {
  226. if _, ok := err.(websocket.HandshakeError); !ok {
  227. log.Println(err)
  228. }
  229. return
  230. }
  231. go writer(ws, httpResult)
  232. reader(ws)
  233. }
  234. }
  235. func latestBeaconWriter(ws *websocket.Conn, latestBeaconsList map[string]model.Beacon, lock *sync.RWMutex) {
  236. pingTicker := time.NewTicker(pingPeriod)
  237. beaconTicker := time.NewTicker(beaconPeriod)
  238. defer func() {
  239. pingTicker.Stop()
  240. beaconTicker.Stop()
  241. ws.Close()
  242. }()
  243. for {
  244. select {
  245. case <-beaconTicker.C:
  246. lock.RLock()
  247. var la = make([]model.Beacon, 0)
  248. for _, b := range latestBeaconsList {
  249. la = append(la, b)
  250. }
  251. lock.RUnlock()
  252. js, err := json.Marshal(la)
  253. if err != nil {
  254. js = []byte("error")
  255. }
  256. ws.SetWriteDeadline(time.Now().Add(writeWait))
  257. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  258. return
  259. }
  260. case <-pingTicker.C:
  261. ws.SetWriteDeadline(time.Now().Add(writeWait))
  262. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  263. return
  264. }
  265. }
  266. }
  267. }
  268. func serveLatestBeaconsWs(latestList *model.LatestBeaconsList) http.HandlerFunc {
  269. return func(w http.ResponseWriter, r *http.Request) {
  270. ws, err := upgrader.Upgrade(w, r, nil)
  271. if err != nil {
  272. if _, ok := err.(websocket.HandshakeError); !ok {
  273. log.Println(err)
  274. }
  275. return
  276. }
  277. go latestBeaconWriter(ws, latestList.LatestList, &latestList.LatestListLock)
  278. reader(ws)
  279. }
  280. }
  281. func handleConnections(clients map[*websocket.Conn]bool, broadcast *chan model.Message) http.HandlerFunc {
  282. return func(w http.ResponseWriter, r *http.Request) {
  283. ws, err := upgrader.Upgrade(w, r, nil)
  284. if err != nil {
  285. log.Fatal(err)
  286. }
  287. defer ws.Close()
  288. clients[ws] = true
  289. for {
  290. var msg model.Message
  291. err := ws.ReadJSON(&msg)
  292. if err != nil {
  293. log.Printf("error: %v", err)
  294. delete(clients, ws)
  295. break
  296. }
  297. *broadcast <- msg
  298. }
  299. }
  300. }
  301. func handleMessages(clients map[*websocket.Conn]bool, broadcast *chan model.Message) {
  302. for {
  303. msg := <-*broadcast
  304. for client := range clients {
  305. err := client.WriteJSON(msg)
  306. if err != nil {
  307. log.Printf("error: %v", err)
  308. client.Close()
  309. delete(clients, client)
  310. }
  311. }
  312. }
  313. }