1 // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
17 "github.com/gorilla/websocket"
21 // Time allowed to write the file to the client.
22 writeWait = 10 * time.Second
24 // Time allowed to read the next pong message from the client.
25 pongWait = 60 * time.Second
27 // Send pings to client with this period. Must be less than pongWait.
28 pingPeriod = (pongWait * 9) / 10
30 // Poll file for changes with this period.
31 filePeriod = 10 * time.Second
35 addr = flag.String("addr", ":8080", "http service address")
36 homeTempl = template.Must(template.New("").Parse(homeHTML))
38 upgrader = websocket.Upgrader{
40 WriteBufferSize: 1024,
44 func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) {
45 fi, err := os.Stat(filename)
47 return nil, lastMod, err
49 if !fi.ModTime().After(lastMod) {
50 return nil, lastMod, nil
52 p, err := ioutil.ReadFile(filename)
54 return nil, fi.ModTime(), err
56 return p, fi.ModTime(), nil
59 func reader(ws *websocket.Conn) {
62 ws.SetReadDeadline(time.Now().Add(pongWait))
63 ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
65 _, _, err := ws.ReadMessage()
72 func writer(ws *websocket.Conn, lastMod time.Time) {
74 pingTicker := time.NewTicker(pingPeriod)
75 fileTicker := time.NewTicker(filePeriod)
87 p, lastMod, err = readFileIfModified(lastMod)
90 if s := err.Error(); s != lastError {
99 ws.SetWriteDeadline(time.Now().Add(writeWait))
100 if err := ws.WriteMessage(websocket.TextMessage, p); err != nil {
105 ws.SetWriteDeadline(time.Now().Add(writeWait))
106 if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
113 func serveWs(w http.ResponseWriter, r *http.Request) {
114 ws, err := upgrader.Upgrade(w, r, nil)
116 if _, ok := err.(websocket.HandshakeError); !ok {
122 var lastMod time.Time
123 if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err == nil {
124 lastMod = time.Unix(0, n)
127 go writer(ws, lastMod)
131 func serveHome(w http.ResponseWriter, r *http.Request) {
132 if r.URL.Path != "/" {
133 http.Error(w, "Not found", http.StatusNotFound)
136 if r.Method != "GET" {
137 http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
140 w.Header().Set("Content-Type", "text/html; charset=utf-8")
141 p, lastMod, err := readFileIfModified(time.Time{})
143 p = []byte(err.Error())
144 lastMod = time.Unix(0, 0)
153 strconv.FormatInt(lastMod.UnixNano(), 16),
155 homeTempl.Execute(w, &v)
160 if flag.NArg() != 1 {
161 log.Fatal("filename not specified")
163 filename = flag.Args()[0]
164 http.HandleFunc("/", serveHome)
165 http.HandleFunc("/ws", serveWs)
166 if err := http.ListenAndServe(*addr, nil); err != nil {
171 const homeHTML = `<!DOCTYPE html>
174 <title>WebSocket Example</title>
177 <pre id="fileData">{{.Data}}</pre>
178 <script type="text/javascript">
180 var data = document.getElementById("fileData");
181 var conn = new WebSocket("ws://{{.Host}}/ws?lastMod={{.LastMod}}");
182 conn.onclose = function(evt) {
183 data.textContent = 'Connection closed';
185 conn.onmessage = function(evt) {
186 console.log('file updated');
187 data.textContent = evt.data;