Introduzione agli eventi inviati dal server (SSE)

Gli eventi inviati dal server (SSE) sono una tecnologia di push server che consente ai client di ricevere automaticamente gli aggiornamenti dal server tramite una connessione HTTP. Descrive come il server inizia il trasferimento dei dati al client dopo aver stabilito la connessione iniziale del client. Sono comunemente utilizzati per inviare aggiornamenti dei messaggi o flussi continui di dati ai client del browser, con l'obiettivo di migliorare i flussi locali tra browser attraverso l'API JavaScript chiamata EventSource. I client possono utilizzare questa API per richiedere un URL specifico per ricevere flussi di eventi. Come parte di HTML5, l'API EventSource è stata standardizzata da WHATWG. Il tipo di supporto per SSE è text/event-stream.

Nota: La differenza più significativa tra SSE e Websocket è che SSE è un push di messaggi dal server al client in un solo senso, mentre Websocket è un push di messaggi in entrambi i sensi. A volte, quando i requisiti aziendali non sono così complessi, il push unidirezionale di SSE è sufficiente. Questo è simile alla tecnologia utilizzata nelle conversazioni AI di ChatGPT.

Esempio di SSE di Fiber

package main

import (
	"bufio"
	"fmt"
	"log"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/valyala/fasthttp"
)

// Simulare il client HTML, ecco un esempio di ricezione di messaggi di push dal backend in JS, in scenari aziendali effettivi, il frontend implementerà questo.
var index = []byte(`<!DOCTYPE html>
<html>
<body>

<h1>Messaggi SSE</h1>
<div id="result"></div>

<script>
if(typeof(EventSource) !== "undefined") {
  var source = new EventSource("http://127.0.0.1:3000/sse");
  source.onmessage = function(event) {
    document.getElementById("result").innerHTML += event.data + "<br>";
  };
} else {
  document.getElementById("result").innerHTML = "Spiacenti, il tuo browser non supporta gli eventi inviati dal server...";
}
</script>

</body>
</html>
`)

func main() {
	// Istanza Fiber
	app := fiber.New()

	// Restrizioni CORS, consentendo l'accesso da qualsiasi dominio
	app.Use(cors.New(cors.Config{
		AllowOrigins:     "*",
		AllowHeaders:     "Cache-Control",
		AllowCredentials: true,
	}))

	// Accedi al percorso /, restituiamo prima la pagina frontend, consentendo al frontend di richiedere al backend di avviare il push dei messaggi SSE
	app.Get("/", func(c *fiber.Ctx) error {
		c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)

		return c.Status(fiber.StatusOK).Send(index)
	})

	// Indirizzo di push dei messaggi SSE
	app.Get("/sse", func(c *fiber.Ctx) error {
		// Imposta l'intestazione http sse, presta attenzione a Content-Type
		c.Set("Content-Type", "text/event-stream")
		c.Set("Cache-Control", "no-cache")
		c.Set("Connection", "keep-alive")
		c.Set("Transfer-Encoding", "chunked")

		// Inizia a inviare messaggi
		c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) {
			fmt.Println("WRITER")
			var i int
			// Simula l'invio continuo di messaggi al client
			for {
				i++
				msg := fmt.Sprintf("%d - l'ora è %v", i, time.Now())
				// Utilizza la funzione writer e Fprintf per inviare messaggi al client, in scenari aziendali effettivi, può essere inviato del testo JSON per la comodità di elaborazione nel frontend
				fmt.Fprintf(w, "data: Messaggio: %s\n\n", msg)
				fmt.Println(msg)

				// Svuota i dati in output al client
				err := w.Flush()
				if err != nil {
					// Aggiornare la pagina nel browser web per stabilire una nuova connessione SSE, ma solo l'ultima è attiva, quindi le connessioni obsolete devono essere chiuse qui.
					fmt.Printf("Errore durante il flushing: %v. Chiusura della connessione http.\n", err)
					break
				}
				time.Sleep(2 * time.Second)
			}
		}))

		return nil
	})

	// Avvia il server
	log.Fatal(app.Listen(":3000"))
}