-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
120 lines (94 loc) · 2.36 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package main
import (
"bytes"
"context"
_ "embed"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"strings"
"time"
)
//go:embed index.html
var indexHTML []byte
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(indexHTML)
})
http.HandleFunc("/crypto-price", func(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported", http.StatusInternalServerError)
return
}
fmt.Println("Request received for price...")
w.Header().Set("Content-Type", "text/event-stream")
priceCh := make(chan int)
// Start a go routine that will send price updates the on the price channel.
// These price updates will be sent back to the client.
go generateCryptoPrice(r.Context(), priceCh)
for price := range priceCh {
event, err := formatServerSentEvent("price-update", price)
if err != nil {
fmt.Println(err)
break
}
_, err = fmt.Fprint(w, event)
if err != nil {
fmt.Println(err)
break
}
flusher.Flush()
}
fmt.Println("Finished sending price updates...")
})
http.ListenAndServe(":4444", nil)
}
// generateCryptoPrice generates price as random integer and sends it the
// provided channel every 1 second.
func generateCryptoPrice(ctx context.Context, priceCh chan<- int) {
r := rand.New(rand.NewSource(time.Now().Unix()))
ticker := time.NewTicker(time.Second)
outerloop:
for {
select {
case <-ctx.Done():
break outerloop
case <-ticker.C:
p := r.Intn(100)
priceCh <- p
}
}
ticker.Stop()
close(priceCh)
fmt.Println("generateCryptoPrice: Finished geenrating")
}
// formatServerSentEvent takes name of an event and any kind of data and transforms
// into a server sent event payload structure.
// Data is sent as a json object, { "data": <your_data> }.
//
// Example:
//
// Input:
// event="price-update"
// data=10
// Output:
// event: price-update\n
// data: "{\"data\":10}"\n\n
func formatServerSentEvent(event string, data any) (string, error) {
m := map[string]any{
"data": data,
}
buff := bytes.NewBuffer([]byte{})
encoder := json.NewEncoder(buff)
err := encoder.Encode(m)
if err != nil {
return "", err
}
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("event: %s\n", event))
sb.WriteString(fmt.Sprintf("data: %v\n\n", buff.String()))
return sb.String(), nil
}