I'm trying to push the time to the browser using channels, so I wrote the below:
package main
import (
"net/http"
"time"
)
type DataPasser struct {
logs chan string
}
func main() {
passer := &DataPasser{logs: make(chan string)}
go func() {
for {
passer.logs <- time.Now().String()
}
}()
http.HandleFunc("/", passer.handleHello)
http.ListenAndServe(":9999", nil)
}
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
for {
w.Write([]byte(<-p.logs))
}
/* for {
io.WriteString(w, <-p.logs)
}
*/
}
It worked by kept adding new lines with each new time, as below:
What I need is to get single line, that is cleared and replaced with the new time everytime the server sending time to it? any help?
UPDATE
I tried using SSE
server sent event, as below but did not work:
package main
import (
"net/http"
"time"
)
type DataPasser struct {
logs chan string
}
func main() {
passer := &DataPasser{logs: make(chan string)}
t := time.NewTicker(time.Second)
defer t.Stop()
go func() {
for range t.C {
passer.logs <- time.Now().String()
}
}()
http.HandleFunc("/", passer.handleHello)
http.ListenAndServe(":9999", nil)
}
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
setupCORS(&w, r)
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Internal error", 500)
return
}
for {
w.Write([]byte(<-p.logs))
// c := []byte(<-p.logs)
// fmt.Fprint(w, c)
flusher.Flush()
}
/* for {
io.WriteString(w, <-p.logs)
}
*/
// w.Write([]byte("Hi, from Service: " ws.name))
}
func setupCORS(w *http.ResponseWriter, req *http.Request) {
(*w).Header().Set("Cache-Control", "no-cache")
(*w).Header().Set("Access-Control-Allow-Origin", "*")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
And the html file as:
<html>
<head></head>
<body>
<div id="counter" width="500" height="600">
</body>
<script>
var source = new EventSource("http://localhost:9999/");
source.onmessage = function (event) {
console.log(event)
var counter = event.data; // JSON.parse(event.data);
document.getElementById("counter").innerHTML = counter;
}
</script>
</html>
CodePudding user response:
The application must write the response in the text/event-stream format:
fmt.Fprintf(w, "data: %s\n\n", <-p.logs)
There are other problems. The handler should exit when the client disconnects or when there's an error writing to the response. The handler should flush the headers before waiting for the first event. Here's the updated code:
func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
setupCORS(w, r)
w.Header().Set("Content-Type", "text/event-stream")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Internal error", 500)
return
}
flusher.Flush()
done := r.Context().Done()
defer fmt.Println("EXIT")
for {
select {
case <-done:
// the client disconnected
return
case m := <-p.logs:
if _, err := fmt.Fprintf(w, "data: %s\n\n", m); err != nil {
// Write to connection failed. Subsequent writes will probably fail.
return
}
flusher.Flush()
}
}
}