// Visualize debug information from hamnet70. // // This program is a HTTP server which reads JSON messages (one per line) from // a FIFO filled by hamnet70 and passes them to the browser via WebSocket. // Concurrent browser sessions and reconnects are supported. This binary is // standalone and can be used without the static/ directory. package main import ( "bufio" "embed" "io/fs" "log" "net/http" "os" "sync" "time" "golang.org/x/net/websocket" ) //go:embed static/* var static embed.FS func main() { if len(os.Args) != 2 && len(os.Args) != 3 { log.SetFlags(0) log.Fatalf("usage: %s []", os.Args[0]) } var mutex sync.Mutex listeners := make(map[*chan string]struct{}) go func() { for { path := os.Args[1] var dupPath string if len(os.Args) == 3 { dupPath = os.Args[2] + "." + time.Now().Format(time.RFC3339) } fh, err := os.Open(path) if err != nil { log.Fatal(err) } var dupFh *os.File if dupPath != "" { dupFh, err = os.Create(dupPath) if err != nil { log.Fatal(err) } } r := bufio.NewReader(fh) for { line, err := r.ReadString('\n') if err != nil { log.Printf("read %q: %v", path, err) break } // Duplicate JSON input to a file so it can be replayed later // if necessary if dupFh != nil { _, err = dupFh.WriteString(line) if err != nil { log.Fatalf("write %q: %v", dupPath, err) } } // Send to all listeners mutex.Lock() for x := range listeners { *x <- line } mutex.Unlock() } err = fh.Close() if err != nil { log.Fatalf("close %q: %v", path, err) } if dupFh != nil { err = dupFh.Close() if err != nil { log.Fatalf("close %q: %v", dupPath, err) } } } }() // Use existing "./static/" directory for quick changes var staticFS http.FileSystem _, err := os.Stat("static") if err == nil { staticFS = http.Dir("static") } else { x, err := fs.Sub(static, "static") if err != nil { log.Fatal(err) } staticFS = http.FS(x) } http.Handle("/", http.FileServer(staticFS)) http.Handle("/events", websocket.Handler(func(conn *websocket.Conn) { // Register new listener for this connection ch := make(chan string, 512) // buffer up to x number of messages mutex.Lock() listeners[&ch] = struct{}{} mutex.Unlock() defer func() { mutex.Lock() delete(listeners, &ch) mutex.Unlock() }() for { x := <-ch err := websocket.Message.Send(conn, x) if err != nil { log.Printf("Websocket error: %v", err) break } } })) err = http.ListenAndServe("localhost:8080", nil) if err != nil { log.Fatal(err) } } // vi: set noet ts=4 sw=4 sts=4: