244 lines
4.7 KiB
Go
244 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gin-contrib/cors"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type Message struct {
|
|
Event string `json:"event"`
|
|
Data string `json:"data"`
|
|
Id string `json:"id"`
|
|
}
|
|
|
|
type ClientChan chan Message
|
|
|
|
type Client struct {
|
|
Id string `json:"id"`
|
|
Ip string `json:"ip"`
|
|
Chan ClientChan `json:"chan"`
|
|
Events []string `json:"events"`
|
|
}
|
|
|
|
type Event struct {
|
|
Clients []Client `json:"clients"`
|
|
Id int `json:"id"`
|
|
Events []string `json:"events"`
|
|
}
|
|
|
|
func main() {
|
|
|
|
r := gin.Default()
|
|
r.Use(cors.Default())
|
|
|
|
event := NewServer()
|
|
|
|
api := r.Group("/api")
|
|
// api.GET("/sse", HeadersMiddleware(), event.serveHTTP(), event.retvalSSE())
|
|
api.GET("/sse", HeadersMiddleware(), event.Stream)
|
|
api.GET("/msg", event.SendMsg)
|
|
api.GET("/message", event.SendMessage)
|
|
api.GET("/ping", event.SendPing)
|
|
api.GET("/pong", event.SendPong)
|
|
|
|
sse := r.Group("/sse")
|
|
sse.Static("/", "./static/sse")
|
|
|
|
app := r.Group("/_app")
|
|
app.Static("/", "./static/_app")
|
|
|
|
static := r.Group("/static")
|
|
static.Static("/", "./static/static")
|
|
|
|
r.StaticFile("/", "./static/index.html")
|
|
r.StaticFile("/favicon.png", "./static/favicon.png")
|
|
|
|
r.ForwardedByClientIP = true
|
|
r.SetTrustedProxies([]string{"127.0.0.1"})
|
|
|
|
r.Run(":8888")
|
|
}
|
|
|
|
func NewServer() *Event {
|
|
return &Event{
|
|
Clients: []Client{},
|
|
Id: 0,
|
|
Events: []string{
|
|
"ping",
|
|
"pong",
|
|
"msg",
|
|
"message",
|
|
},
|
|
}
|
|
}
|
|
|
|
func HeadersMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
headers := c.Writer.Header()
|
|
headers.Set("Content-Type", "text/event-stream")
|
|
headers.Set("Cache-Control", "no-cache")
|
|
headers.Set("Connection", "keep-alive")
|
|
headers.Set("Transfer-Encoding", "chunked")
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func (e *Event) Stream(c *gin.Context) {
|
|
client := &Client{
|
|
Ip: c.Request.RemoteAddr,
|
|
Chan: make(ClientChan),
|
|
Events: c.QueryArray("event"),
|
|
}
|
|
|
|
client.Id = Id(client.Ip, client.Events, 8)
|
|
|
|
log.Printf("Client: %+v", client)
|
|
|
|
e.Clients = append(e.Clients, *client)
|
|
|
|
defer func() {
|
|
e.Clients = remove(e.Clients, client)
|
|
close(client.Chan)
|
|
log.Printf("Client %s disconnected - tot: %d", client.Ip, len(e.Clients))
|
|
}()
|
|
|
|
log.Printf("Client %s connected - tot: %d", client.Ip, len(e.Clients))
|
|
|
|
c.Stream(func(w io.Writer) bool {
|
|
msg, ok := <-client.Chan
|
|
if ok {
|
|
c.SSEvent(msg.Event, msg)
|
|
}
|
|
return ok
|
|
})
|
|
}
|
|
|
|
func (e *Event) Send(msg Message) {
|
|
e.Id++
|
|
msg.Id = strconv.Itoa(e.Id)
|
|
for _, client := range e.Clients {
|
|
if slices.Contains(client.Events, msg.Event) || slices.Contains(client.Events, "*") {
|
|
client.Chan <- msg
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *Event) SendMessage(c *gin.Context) {
|
|
e.Send(Message{
|
|
Event: "message",
|
|
Data: "Ciao",
|
|
})
|
|
e.Send(Message{
|
|
Data: "Senza event",
|
|
})
|
|
}
|
|
|
|
func (e *Event) SendMsg(c *gin.Context) {
|
|
e.Send(Message{
|
|
Event: "msg",
|
|
Data: "hello",
|
|
})
|
|
c.JSON(http.StatusOK, gin.H{"status": "msg"})
|
|
}
|
|
|
|
func (e *Event) SendPing(c *gin.Context) {
|
|
e.Send(Message{
|
|
Event: "ping",
|
|
Data: "Ping",
|
|
})
|
|
c.JSON(http.StatusOK, gin.H{"status": "ping"})
|
|
}
|
|
|
|
func (e *Event) SendPong(c *gin.Context) {
|
|
e.Send(Message{
|
|
Event: "pong",
|
|
Data: "Pong",
|
|
})
|
|
c.JSON(http.StatusOK, gin.H{"status": "pong"})
|
|
}
|
|
|
|
func remove(slice []Client, s *Client) []Client {
|
|
for i, v := range slice {
|
|
if v.Id == s.Id {
|
|
return append(slice[:i], slice[i+1:]...)
|
|
}
|
|
}
|
|
return slice
|
|
}
|
|
|
|
func Id(ip string, events []string, len int) string {
|
|
str := ip + strings.Join(events, "|")
|
|
id := sha256.Sum256([]byte(str))
|
|
|
|
return fmt.Sprintf("%X", id)[0:len]
|
|
}
|
|
|
|
// func (e *Event) serveHTTP() gin.HandlerFunc {
|
|
// return func(c *gin.Context) {
|
|
// // Initialize client channel
|
|
// clientChan := make(ClientChan)
|
|
|
|
// // Send new connection to event server
|
|
// e.NewClient <- clientChan
|
|
|
|
// defer func() {
|
|
// // Send closed connection to event server
|
|
// e.ClosedClient <- clientChan
|
|
// }()
|
|
|
|
// c.Set("clientChan", clientChan)
|
|
|
|
// c.Next()
|
|
// }
|
|
// }
|
|
|
|
// func (e *Event) retvalSSE() gin.HandlerFunc {
|
|
// return func(c *gin.Context) {
|
|
// v, ok := c.Get("clientChan")
|
|
// if !ok {
|
|
// return
|
|
// }
|
|
|
|
// clientChan, ok := v.(ClientChan)
|
|
// if !ok {
|
|
// return
|
|
// }
|
|
|
|
// c.Stream(func(w io.Writer) bool {
|
|
// msg, ok := <-clientChan
|
|
// if ok {
|
|
// c.SSEvent("message", msg)
|
|
// }
|
|
// return ok
|
|
// })
|
|
// }
|
|
// }
|
|
|
|
// func (e *Event) Broadcast() {
|
|
// id := 0
|
|
// for {
|
|
// select {
|
|
// case client := <-e.NewClient:
|
|
// e.TotalClients[client] = true
|
|
// case client := <-e.ClosedClient:
|
|
// delete(e.TotalClients, client)
|
|
// close(client)
|
|
// case message := <-e.Message:
|
|
// id++
|
|
// for client := range e.TotalClients {
|
|
// // msg, _ := json.Marshal(message)
|
|
// // client <- string(msg)
|
|
// client <- message
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|