From d2cd84de2ba905ee5d7f95f671c7476d21b8ef4e Mon Sep 17 00:00:00 2001 From: Lukas Martinelli Date: Sun, 1 Jan 2017 15:50:58 +0100 Subject: [PATCH] Switch filewatch implementation to fsnotify --- .travis.yml | 1 + README.md | 1 + appveyor.yml | 1 + filewatch/filewatch.go | 123 ++++++++++++----------------------------- 4 files changed, 38 insertions(+), 88 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1f1c974..17bf4aec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ os: - osx language: go install: +- go get github.com/fsnotify/fsnotify - go get github.com/gorilla/handlers - go get github.com/gorilla/mux - go get github.com/gorilla/websocket diff --git a/README.md b/README.md index 10481eb9..3b0e2b28 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Install the 3rd party dependencies. go get github.com/gorilla/handlers go get github.com/gorilla/mux go get github.com/gorilla/websocket +go get github.com/fsnotify/fsnotify go get github.com/urfave/cli go get github.com/elazarl/go-bindata-assetfs/... go get github.com/jteeuwen/go-bindata/... diff --git a/appveyor.yml b/appveyor.yml index 4d07c960..6fbda7ca 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ install: - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% - go version - go env +- go get github.com/fsnotify/fsnotify - go get github.com/gorilla/handlers - go get github.com/gorilla/mux - go get github.com/gorilla/websocket diff --git a/filewatch/filewatch.go b/filewatch/filewatch.go index d00c0f78..4248212e 100644 --- a/filewatch/filewatch.go +++ b/filewatch/filewatch.go @@ -4,103 +4,55 @@ import ( "io/ioutil" "log" "net/http" - "os" - "strconv" - "time" + "github.com/fsnotify/fsnotify" "github.com/gorilla/websocket" ) -// Adapted from https://github.com/gorilla/websocket -// Copyright (c) 2013 The Gorilla WebSocket Authors -// https://github.com/gorilla/websocket/blob/master/examples/filewatch/main.go - -const ( - // Time allowed to write the file to the client. - writeWait = 10 * time.Second - - // Time allowed to read the next pong message from the client. - pongWait = 60 * time.Second - - // Send pings to client with this period. Must be less than pongWait. - pingPeriod = (pongWait * 9) / 10 - - // Poll file for changes with this period. - filePeriod = 10 * time.Second -) - var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { return true }, } -func readFileIfModified(filename string, lastMod time.Time) ([]byte, time.Time, error) { - fi, err := os.Stat(filename) +func writer(ws *websocket.Conn, filename string) { + watcher, err := fsnotify.NewWatcher() if err != nil { - return nil, lastMod, err + log.Fatal(err) } - if !fi.ModTime().After(lastMod) { - return nil, lastMod, nil - } - p, err := ioutil.ReadFile(filename) - if err != nil { - return nil, fi.ModTime(), err - } - return p, fi.ModTime(), nil -} + defer watcher.Close() -func reader(ws *websocket.Conn) { - defer ws.Close() - ws.SetReadLimit(512) - ws.SetReadDeadline(time.Now().Add(pongWait)) - ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) - for { - _, _, err := ws.ReadMessage() - if err != nil { - break + done := make(chan bool) + go func() { + for { + select { + case event := <-watcher.Events: + if event.Op&fsnotify.Write == fsnotify.Write { + log.Println("Modified file:", event.Name) + var p []byte + var err error + + p, err = ioutil.ReadFile(filename) + if err != nil { + log.Fatal(err) + } + + if p != nil { + if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { + return + } + } + } + case err := <-watcher.Errors: + log.Println("Watch error:", err) + } } - } -} - -func writer(ws *websocket.Conn, filename string, lastMod time.Time) { - lastError := "" - pingTicker := time.NewTicker(pingPeriod) - fileTicker := time.NewTicker(filePeriod) - defer func() { - pingTicker.Stop() - fileTicker.Stop() - ws.Close() }() - for { - select { - case <-fileTicker.C: - var p []byte - var err error - p, lastMod, err = readFileIfModified(filename, lastMod) - - if err != nil { - if s := err.Error(); s != lastError { - lastError = s - p = []byte(lastError) - } - } else { - lastError = "" - } - - if p != nil { - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { - return - } - } - case <-pingTicker.C: - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { - return - } - } + if err = watcher.Add(filename); err != nil { + log.Fatal(err) } + <-done } func ServeWebsocketFileWatcher(filename string, w http.ResponseWriter, r *http.Request) { @@ -112,11 +64,6 @@ func ServeWebsocketFileWatcher(filename string, w http.ResponseWriter, r *http.R return } - var lastMod time.Time - if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil { - lastMod = time.Unix(0, n) - } - - go writer(ws, filename, lastMod) - reader(ws) + writer(ws, filename) + defer ws.Close() }