lipre.go (4128B)
1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "net/http" 8 "strconv" 9 "sync" 10 11 "github.com/gorilla/mux" 12 "github.com/gorilla/websocket" 13 ) 14 15 // Not used - either read password from config / flag or define the valid rooms in a file on server 16 const temporaryCorrectRoomCode = "tester" 17 18 type File struct { 19 Name string `json:"name"` 20 Contents string `json:"contents"` 21 } 22 23 type Room struct { 24 mu sync.Mutex 25 code string 26 presenter *websocket.Conn 27 viewers []*websocket.Conn 28 // Store files so that they can be sent to new viewers upon connection 29 files map[string]File 30 // Number of minutes for which the room should continue to be open after the presenter disconnects 31 linger int 32 } 33 34 var rooms = make(map[string]*Room) 35 36 // Thread safe Room functions. 37 // The rooms map should only be written to from these 38 39 func (room *Room) open() { 40 room.mu.Lock() 41 defer room.mu.Unlock() 42 existingRoom := rooms[room.code] 43 if existingRoom != nil { 44 existingRoom.close() 45 } 46 rooms[room.code] = room 47 room.presenter.SetCloseHandler(func(code int, text string) error { 48 room.close() 49 return nil 50 }) 51 go room.listen() 52 } 53 54 func (room *Room) close() { 55 room.mu.Lock() 56 defer room.mu.Unlock() 57 // The actual room close code is handled in the presenter connection close handler 58 fmt.Printf("Closing room '%v'\n", room.code) 59 for _, viewer := range room.viewers { 60 if viewer != nil { 61 viewer.Close() 62 } 63 } 64 room.presenter.Close() 65 delete(rooms, room.code) 66 } 67 68 func (room *Room) addViewer(viewerConn *websocket.Conn) { 69 room.mu.Lock() 70 room.viewers = append(room.viewers, viewerConn) 71 room.mu.Unlock() 72 // And write all existing files 73 for _, filedata := range room.files { 74 viewerConn.WriteJSON(&filedata) 75 } 76 } 77 78 func (room *Room) listen() { 79 for { 80 var file File 81 err := room.presenter.ReadJSON(&file) 82 if err != nil { 83 if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure, websocket.CloseAbnormalClosure) { 84 log.Printf("Unexpected close error: %v", err) 85 } 86 // TODO: Handle JSON errors (send info to presenter) 87 return 88 } 89 room.files[file.Name] = file 90 for _, viewerConn := range room.viewers { 91 if viewerConn == nil { 92 break 93 } 94 viewerConn.WriteJSON(&file) 95 } 96 } 97 } 98 99 // HTTP 100 var wsUpgrader = websocket.Upgrader{ 101 ReadBufferSize: 1024, 102 WriteBufferSize: 1024, 103 } 104 105 // fileHandler looks in ui/dist directory for static files matching the path 106 // Writes a 404 message if not found 107 func fileHandler(w http.ResponseWriter, r *http.Request) { 108 filePath := r.URL.Path 109 if filePath == "/" { 110 filePath = "/index.html" 111 } 112 // Check if the file exists among the static assets 113 // At time of writing, this is only true for index.html and lipre.js, 114 // but code splitting may be introduced and change that 115 htmlData, err := ioutil.ReadFile(fmt.Sprintf("ui/dist%v", filePath)) 116 if err != nil { 117 w.WriteHeader(http.StatusNotFound) 118 w.Write([]byte("404 :(")) 119 return 120 } 121 w.Write(htmlData) 122 return 123 } 124 125 func presentHandler(w http.ResponseWriter, r *http.Request) { 126 roomCode := mux.Vars(r)["roomCode"] 127 qparams := r.URL.Query() 128 pLinger := qparams["linger"] 129 var iLinger int 130 if len(pLinger) == 1 { 131 iLinger, _ = strconv.Atoi(pLinger[0]) 132 } 133 /*if roomCode != temporaryCorrectRoomCode { 134 w.WriteHeader(http.StatusBadRequest) 135 return 136 }*/ 137 fmt.Println("Upgrading connection") 138 conn, err := wsUpgrader.Upgrade(w, r, nil) 139 if err != nil { 140 log.Println(err) 141 return 142 } 143 room := &Room{code: roomCode, presenter: conn, linger: iLinger, files: make(map[string]File)} 144 room.open() 145 } 146 147 func viewHandler(w http.ResponseWriter, r *http.Request) { 148 roomCode := mux.Vars(r)["roomCode"] 149 room := rooms[roomCode] 150 if room == nil { 151 return 152 } 153 conn, err := wsUpgrader.Upgrade(w, r, nil) 154 if err != nil { 155 log.Println(err) 156 return 157 } 158 room.addViewer(conn) 159 } 160 161 func main() { 162 fmt.Println("Server starting") 163 router := mux.NewRouter() 164 router.HandleFunc("/ws/pres/{roomCode}", presentHandler) 165 router.HandleFunc("/ws/view/{roomCode}", viewHandler) 166 router.PathPrefix("/").HandlerFunc(fileHandler) 167 http.Handle("/", router) 168 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", 8080), nil)) 169 }