package main import ( "encoding/json" "io" "log" "net" "net/http" "os" "strings" "sync" "github.com/gorilla/websocket" ) var upgrader = websocket.Upgrader{ WriteBufferSize: 4096, ReadBufferSize: 0, CheckOrigin: func(r *http.Request) bool { return true }, } type client struct { nudges chan [2]int quit chan bool } type clientsStore struct { set map[*client]struct{} sync.RWMutex } func main() { var clients clientsStore clients.set = make(map[*client]struct{}) mux := http.NewServeMux() mux.HandleFunc("/controller", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": var nudge [2]int switch r.Header.Get("Content-Type") { case "application/json": var decoder = json.NewDecoder(r.Body) if err := decoder.Decode(&nudge); err != nil { log.Println(err) w.WriteHeader(http.StatusBadRequest) return } default: w.WriteHeader(http.StatusBadRequest) return } (func() { clients.RLock() defer clients.RUnlock() for c, _ := range clients.set { select { case c.nudges <- nudge: default: log.Println("Client too slow -> dropping nudge") } } })() w.WriteHeader(http.StatusNoContent) case "GET": w.Header().Add("Content-Type", "text/html") w.WriteHeader(http.StatusOK) file, _ := os.Open("./controller/controller.html") defer file.Close() io.Copy(w, file) default: w.WriteHeader(http.StatusMethodNotAllowed) } }) mux.HandleFunc("/controller/main.css", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "text/css") w.WriteHeader(http.StatusOK) file, _ := os.Open("./display/main.css") defer file.Close() io.Copy(w, file) }) mux.HandleFunc("/controller/controller.js", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/javascript") w.WriteHeader(http.StatusOK) file, _ := os.Open("./controller/controller.js") defer file.Close() io.Copy(w, file) }) mux.Handle("/", http.FileServer(http.Dir("./display/"))) mux.HandleFunc("/socket", func(w http.ResponseWriter, r *http.Request) { con, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } defer con.Close() var c client c.nudges = make(chan [2]int) (func() { clients.Lock() defer clients.Unlock() clients.set[&c] = struct{}{} })() defer (func() { clients.Lock() defer clients.Unlock() delete(clients.set, &c) log.Println("Closing client connection") })() con.SetCloseHandler(func(code int, text string) error { close(c.nudges) return nil }) for n := range c.nudges { err := con.WriteJSON(n) if err != nil { log.Println(err) return } } }) var addr string if env, ok := os.LookupEnv("FANCYN_LISTEN"); ok { addr = env } else { addr = "[::1]:8080" } var listener net.Listener if addr == "inetd" { if l, err := net.FileListener(os.Stdin); err == nil { listener = l } else { log.Fatal(err) } } else if strings.HasPrefix(addr, "unix:") { if l, err := net.Listen("unix", strings.TrimPrefix(addr, "unix:")); err == nil { listener = l } else { log.Fatal(err) } } else { if l, err := net.Listen("tcp", addr); err == nil { listener = l } else { log.Fatal(err) } } defer listener.Close() log.Printf("Starting server on %s\n", addr) log.Println(http.Serve(listener, mux)) }