Skip to content

Commit f807cf7

Browse files
authored
Merge pull request #2869 from djames-bloom/feat/1533-sse-user-events
feat: Improve SSE example to demonstrate user-defined publishing
2 parents 7a2b197 + 663a931 commit f807cf7

File tree

2 files changed

+101
-13
lines changed

2 files changed

+101
-13
lines changed

sse/README.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,53 @@ Server-Sent Events (SSE) allow servers to push updates to the client over a sing
4545

4646
- **GET /**: Index page
4747
- **GET /sse**: SSE route
48+
- **PUT /publish**: Send messages via SSE
4849

4950
## Example Usage
5051

52+
By default, the example will run on port `3000`; this can be changed by modifying the `appPort` constant in `main.go`
53+
5154
1. Open your browser and navigate to `http://localhost:3000`.
5255
2. The client will automatically connect to the SSE endpoint and start receiving updates from the server.
56+
3. The `/sse` endpoint will publish the current time to the client every two seconds
57+
58+
### Custom Messages
59+
60+
To send a custom message, send a `PUT` request to the `/publish` endpoint in the following JSON format
61+
62+
```json
63+
{
64+
"message": "Hello, World!"
65+
}
66+
```
67+
68+
Messages sent to the `/publish` endpoint will be added to a queue that is read from in FIFO order. You can test this
69+
by using curl in an iterator
70+
71+
If you are using the Bash or Zsh shell:
72+
```sh
73+
for i in {1..10}; do
74+
curl -X PUT -H 'Content-type: application/json' --data "{\"message\":\"SSE TEST $i\"}" http://localhost:3000/publish
75+
done
76+
```
77+
78+
If you are using fish:
79+
```sh
80+
for i in (seq 1 10)
81+
curl -X PUT -H 'Content-type: application/json' --data "{\"message\":\"SSE TEST $i\"}" http://localhost:3000/publish
82+
end
83+
```
84+
85+
Once published, your added messages will begin appearing in the output at `http://localhost:3000`. Once the queue is empty
86+
and no user-published messages are left, `/sse` will return to it's standard behavior of displaying the current time.
87+
5388
5489
## Code Overview
5590
5691
### `main.go`
5792
5893
The main Go file sets up the Fiber application and handles the SSE connections. It includes the necessary configuration to send events to the client.
5994
60-
### `index.html`
61-
62-
The HTML file provides a simple user interface to connect to the SSE endpoint and display the received messages.
63-
6495
## Additional Information
6596
6697
Server-Sent Events (SSE) is a standard allowing servers to push data to web clients over HTTP. Unlike WebSockets, which require a full-duplex connection, SSE uses a unidirectional connection from the server to the client. This makes SSE simpler to implement and more efficient for scenarios where only the server needs to send updates.

sse/main.go

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ package main
22

33
import (
44
"bufio"
5+
"bytes"
56
"fmt"
67
"log"
8+
"text/template"
79
"time"
810

911
"github.com/gofiber/fiber/v2"
1012
"github.com/gofiber/fiber/v2/middleware/cors"
1113
"github.com/valyala/fasthttp"
1214
)
1315

14-
var index = []byte(`<!DOCTYPE html>
16+
// appPort is the port that the server will listen on
17+
const appPort = "3000"
18+
19+
// index is the HTML template that will be served to the client on the index page (`/`)
20+
const index = `<!DOCTYPE html>
1521
<html>
1622
<body>
1723
@@ -20,7 +26,7 @@ var index = []byte(`<!DOCTYPE html>
2026
2127
<script>
2228
if(typeof(EventSource) !== "undefined") {
23-
var source = new EventSource("http://127.0.0.1:3000/sse");
29+
var source = new EventSource("http://127.0.0.1:{{.Port}}/sse");
2430
source.onmessage = function(event) {
2531
document.getElementById("result").innerHTML += event.data + "<br>";
2632
};
@@ -31,23 +37,43 @@ if(typeof(EventSource) !== "undefined") {
3137
3238
</body>
3339
</html>
34-
`)
40+
`
3541

3642
func main() {
43+
// create a queue to store incoming messages from the
44+
// `/publish` endpoint
45+
var sseMessageQueue []string
46+
3747
// Fiber instance
3848
app := fiber.New()
3949

4050
// CORS for external resources
4151
app.Use(cors.New(cors.Config{
42-
AllowOrigins: "*",
43-
AllowHeaders: "Cache-Control",
44-
AllowCredentials: true,
52+
AllowOrigins: "*",
53+
AllowHeaders: "Cache-Control",
4554
}))
4655

4756
app.Get("/", func(c *fiber.Ctx) error {
4857
c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8)
4958

50-
return c.Status(fiber.StatusOK).Send(index)
59+
tpl, err := template.New("index").Parse(index)
60+
if err != nil {
61+
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
62+
}
63+
64+
data := struct {
65+
Port string
66+
}{
67+
Port: appPort,
68+
}
69+
70+
buf := new(bytes.Buffer)
71+
err = tpl.Execute(buf, data)
72+
if err != nil {
73+
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
74+
}
75+
76+
return c.Status(fiber.StatusOK).Send(buf.Bytes())
5177
})
5278

5379
app.Get("/sse", func(c *fiber.Ctx) error {
@@ -61,7 +87,19 @@ func main() {
6187
var i int
6288
for {
6389
i++
64-
msg := fmt.Sprintf("%d - the time is %v", i, time.Now())
90+
91+
var msg string
92+
93+
// if there are messages that have been sent to the `/publish` endpoint
94+
// then use these first, otherwise just send the current time
95+
if len(sseMessageQueue) > 0 {
96+
msg = fmt.Sprintf("%d - message recieved: %s", i, sseMessageQueue[0])
97+
// remove the message from the buffer
98+
sseMessageQueue = sseMessageQueue[1:]
99+
} else {
100+
msg = fmt.Sprintf("%d - the time is %v", i, time.Now())
101+
}
102+
65103
fmt.Fprintf(w, "data: Message: %s\n\n", msg)
66104
fmt.Println(msg)
67105

@@ -81,6 +119,25 @@ func main() {
81119
return nil
82120
})
83121

122+
// Publish endpoint adds messages to the queue that will be sent to the client
123+
// via the `/sse` endpoint in FIFO order. If there are no messages in the queue
124+
// then the current time will be sent to the client instead.
125+
app.Put("/publish", func(c *fiber.Ctx) error {
126+
type Message struct {
127+
Message string `json:"message"`
128+
}
129+
130+
payload := new(Message)
131+
132+
if err := c.BodyParser(payload); err != nil {
133+
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
134+
}
135+
136+
sseMessageQueue = append(sseMessageQueue, payload.Message)
137+
138+
return c.SendString("Message added to queue\n")
139+
})
140+
84141
// Start server
85-
log.Fatal(app.Listen(":3000"))
142+
log.Fatal(app.Listen(":" + appPort))
86143
}

0 commit comments

Comments
 (0)