Home > database >  Pushing time to browser using channel
Pushing time to browser using channel

Time:06-13

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: enter image description here

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()
        }
    }
}
  •  Tags:  
  • go
  • Related