From 63204ad3566e6d4fb5f956a4c33e3eaaefe72741 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 11:42:33 +0200 Subject: [PATCH 01/18] transfer ikisocket to contrib --- ikisocket/README.md | 315 ++++++++++++++++++ ikisocket/go.mod | 13 + ikisocket/go.sum | 99 ++++++ ikisocket/ikisocket.go | 615 ++++++++++++++++++++++++++++++++++++ ikisocket/ikisocket_test.go | 426 +++++++++++++++++++++++++ 5 files changed, 1468 insertions(+) create mode 100644 ikisocket/README.md create mode 100644 ikisocket/go.mod create mode 100644 ikisocket/go.sum create mode 100644 ikisocket/ikisocket.go create mode 100644 ikisocket/ikisocket_test.go diff --git a/ikisocket/README.md b/ikisocket/README.md new file mode 100644 index 00000000..29e289eb --- /dev/null +++ b/ikisocket/README.md @@ -0,0 +1,315 @@ + +--- + +id: ikisocket +--- + +# Ikisocket + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=ikisocket*) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired by [Socket.io](https://github.com/socketio/socket.io) + +**Note: Requires Go 1.18 and above** + +## Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/ikisocket +``` + +## Signatures + +```go +// Initialize new ikisocket in the callback this will +// execute a callback that expects kws *Websocket Object +func New(callback func(kws *Websocket)) func(*fiber.Ctx) error +``` + +```go +// Add listener callback for an event into the listeners list +func On(event string, callback func(payload *EventPayload)) +``` + +```go +// Emit the message to a specific socket uuids list +// Ignores all errors +func EmitToList(uuids []string, message []byte) +``` + +```go +// Emit to a specific socket connection +func EmitTo(uuid string, message []byte) error +``` + +```go +// Broadcast to all the active connections +// except avoid broadcasting the message to itself +func Broadcast(message []byte) +``` + +```go +// Fire custom event on all connections +func Fire(event string, data []byte) +``` + + +## Example + +```go +package main + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/gofiber/contrib/ikisocket" + "github.com/gofiber/contrib/websocket" + "github.com/gofiber/fiber/v2" +) + +// MessageObject Basic chat message object +type MessageObject struct { + Data string `json:"data"` + From string `json:"from"` + Event string `json:"event"` + To string `json:"to"` +} + +func main() { + + // The key for the map is message.to + clients := make(map[string]string) + + // Start a new Fiber application + app := fiber.New() + + // Setup the middleware to retrieve the data sent in first GET request + app.Use(func(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + // Multiple event handling supported + ikisocket.On(ikisocket.EventConnect, func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // Custom event handling supported + ikisocket.On("CUSTOM_EVENT", func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + // ---> + + // DO YOUR BUSINESS HERE + + // ---> + }) + + // On message event + ikisocket.On(ikisocket.EventMessage, func(ep *ikisocket.EventPayload) { + + fmt.Println(fmt.Sprintf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))) + + message := MessageObject{} + + // Unmarshal the json message + // { + // "from": "", + // "to": "", + // "event": "CUSTOM_EVENT", + // "data": "hello" + //} + err := json.Unmarshal(ep.Data, &message) + if err != nil { + fmt.Println(err) + return + } + + // Fire custom event based on some + // business logic + if message.Event != "" { + ep.Kws.Fire(message.Event, []byte(message.Data)) + } + + // Emit the message directly to specified user + err = ep.Kws.EmitTo(clients[message.To], ep.Data, ikisocket.TextMessage) + if err != nil { + fmt.Println(err) + } + }) + + // On disconnect event + ikisocket.On(ikisocket.EventDisconnect, func(ep *ikisocket.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On close event + // This event is called when the server disconnects the user actively with .Close() method + ikisocket.On(ikisocket.EventClose, func(ep *ikisocket.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On error event + ikisocket.On(ikisocket.EventError, func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + app.Get("/ws/:id", ikisocket.New(func(kws *ikisocket.Websocket) { + + // Retrieve the user id from endpoint + userId := kws.Params("id") + + // Add the connection to the list of the connected clients + // The UUID is generated randomly and is the key that allow + // ikisocket to manage Emit/EmitTo/Broadcast + clients[userId] = kws.UUID + + // Every websocket connection has an optional session key => value storage + kws.SetAttribute("user_id", userId) + + //Broadcast to all the connected users the newcomer + kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, ikisocket.TextMessage) + //Write welcome message + kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), ikisocket.TextMessage) + })) + + log.Fatal(app.Listen(":3000")) +} + +``` + +--- + + +## Supported events + +```go +// Supported event list +const ( + // Fired when a Text/Binary message is received + EventMessage = "message" + // More details here: + // @url https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets + EventPing = "ping" + EventPong = "pong" + // Fired on disconnection + // The error provided in disconnection event + // is defined in RFC 6455, section 11.7. + // @url https://github.com/gofiber/websocket/blob/cd4720c435de415b864d975a9ca23a47eaf081ef/websocket.go#L192 + EventDisconnect = "disconnect" + // Fired on first connection + EventConnect = "connect" + // Fired when the connection is actively closed from the server + EventClose = "close" + // Fired when some error appears useful also for debugging websockets + EventError = "error" +) +``` + +## Event Payload object + +```go +// Event Payload is the object that +// stores all the information about the event and +// the connection +type EventPayload struct { + // The connection object + Kws *Websocket + // The name of the event + Name string + // Unique connection UUID + SocketUUID string + // Optional websocket attributes + SocketAttributes map[string]string + // Optional error when are fired events like + // - Disconnect + // - Error + Error error + // Data is used on Message and on Error event + Data []byte +} +``` + +--- + +**The FastHTTP connection can be accessed directly from the struct** + +```go +type Websocket struct { + // The FastHTTP connection + Conn *websocket.Conn +} +``` + +Can be accessed from + +```go +kws.Conn +``` + +## Socket instance functions + +```go +// Set a specific attribute for the specific socket connection +func (kws *Websocket) SetAttribute(key string, attribute string) +``` + +```go +// Get socket connection UUID +func (kws *Websocket) GetUUID() string +``` + +```go +// Set socket connection UUID +func (kws *Websocket) SetUUID(uuid string) +``` + +```go +// Get a specific attribute from the socket attributes +func (kws *Websocket) GetAttribute(key string) string +``` + +```go +// Emit the message to a specific socket uuids list +func (kws *Websocket) EmitToList(uuids []string, message []byte) +``` + +```go +// Emit to a specific socket connection +func (kws *Websocket) EmitTo(uuid string, message []byte) error +``` + +```go +// Broadcast to all the active connections +// except avoid broadcasting the message to itself +func (kws *Websocket) Broadcast(message []byte, except bool) +``` + +```go +// Fire custom event +func (kws *Websocket) Fire(event string, data []byte) +``` + +```go +// Emit/Write the message into the given connection +func (kws *Websocket) Emit(message []byte) +``` + +```go +// Actively close the connection from the server +func (kws *Websocket) Close() +``` \ No newline at end of file diff --git a/ikisocket/go.mod b/ikisocket/go.mod new file mode 100644 index 00000000..09f457f4 --- /dev/null +++ b/ikisocket/go.mod @@ -0,0 +1,13 @@ +module github.com/antoniodipinto/ikisocket + +go 1.18 + +require ( + github.com/fasthttp/websocket v1.5.4 + github.com/gofiber/contrib/websocket v1.2.0 + github.com/gofiber/fiber/v2 v2.50.0 + github.com/google/uuid v1.3.1 + github.com/rivo/uniseg v0.4.4 // indirect + github.com/stretchr/testify v1.8.4 + github.com/valyala/fasthttp v1.50.0 +) diff --git a/ikisocket/go.sum b/ikisocket/go.sum new file mode 100644 index 00000000..dccf4c25 --- /dev/null +++ b/ikisocket/go.sum @@ -0,0 +1,99 @@ +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fasthttp/websocket v1.5.4 h1:Bq8HIcoiffh3pmwSKB8FqaNooluStLQQxnzQspMatgI= +github.com/fasthttp/websocket v1.5.4/go.mod h1:R2VXd4A6KBspb5mTrsWnZwn6ULkX56/Ktk8/0UNSJao= +github.com/gofiber/contrib/websocket v1.2.0 h1:E+GNxglSApjJCPwH1y3wLz69c1PuSvADwhMBeDc8Xxc= +github.com/gofiber/contrib/websocket v1.2.0/go.mod h1:Sf8RYFluiIKxONa/Kq0jk05EOUtqrb81pJopTxzcsX4= +github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= +github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= +github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ikisocket/ikisocket.go b/ikisocket/ikisocket.go new file mode 100644 index 00000000..e443b600 --- /dev/null +++ b/ikisocket/ikisocket.go @@ -0,0 +1,615 @@ +package ikisocket + +import ( + "context" + "errors" + "sync" + "time" + + "github.com/gofiber/contrib/websocket" + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" +) + +// Source @url:https://github.com/gorilla/websocket/blob/master/conn.go#L61 +// The message types are defined in RFC 6455, section 11.8. +const ( + // TextMessage denotes a text data message. The text message payload is + // interpreted as UTF-8 encoded text data. + TextMessage = 1 + // BinaryMessage denotes a binary data message. + BinaryMessage = 2 + // CloseMessage denotes a close control message. The optional message + // payload contains a numeric code and text. Use the FormatCloseMessage + // function to format a close message payload. + CloseMessage = 8 + // PingMessage denotes a ping control message. The optional message payload + // is UTF-8 encoded text. + PingMessage = 9 + // PongMessage denotes a pong control message. The optional message payload + // is UTF-8 encoded text. + PongMessage = 10 +) + +// Supported event list +const ( + // EventMessage Fired when a Text/Binary message is received + EventMessage = "message" + // EventPing More details here: + // @url https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets + EventPing = "ping" + EventPong = "pong" + // EventDisconnect Fired on disconnection + // The error provided in disconnection event + // is defined in RFC 6455, section 11.7. + // @url https://github.com/gofiber/websocket/blob/cd4720c435de415b864d975a9ca23a47eaf081ef/websocket.go#L192 + EventDisconnect = "disconnect" + // EventConnect Fired on first connection + EventConnect = "connect" + // EventClose Fired when the connection is actively closed from the server + EventClose = "close" + // EventError Fired when some error appears useful also for debugging websockets + EventError = "error" +) + +var ( + // ErrorInvalidConnection The addressed Conn connection is not available anymore + // error data is the uuid of that connection + ErrorInvalidConnection = errors.New("message cannot be delivered invalid/gone connection") + // ErrorUUIDDuplication The UUID already exists in the pool + ErrorUUIDDuplication = errors.New("UUID already exists in the available connections pool") +) + +var ( + PongTimeout = 1 * time.Second + // RetrySendTimeout retry after 20 ms if there is an error + RetrySendTimeout = 20 * time.Millisecond + //MaxSendRetry define max retries if there are socket issues + MaxSendRetry = 5 + // ReadTimeout Instead of reading in a for loop, try to avoid full CPU load taking some pause + ReadTimeout = 10 * time.Millisecond +) + +// Raw form of websocket message +type message struct { + // Message type + mType int + // Message data + data []byte + // Message send retries when error + retries int +} + +// EventPayload Event Payload is the object that +// stores all the information about the event and +// the connection +type EventPayload struct { + // The connection object + Kws *Websocket + // The name of the event + Name string + // Unique connection UUID + SocketUUID string + // Optional websocket attributes + SocketAttributes map[string]interface{} + // Optional error when are fired events like + // - Disconnect + // - Error + Error error + // Data is used on Message and on Error event + Data []byte +} + +type ws interface { + IsAlive() bool + GetUUID() string + SetUUID(uuid string) + SetAttribute(key string, attribute interface{}) + GetAttribute(key string) interface{} + GetIntAttribute(key string) int + GetStringAttribute(key string) string + EmitToList(uuids []string, message []byte, mType ...int) + EmitTo(uuid string, message []byte, mType ...int) error + Broadcast(message []byte, except bool, mType ...int) + Fire(event string, data []byte) + Emit(message []byte, mType ...int) + Close() + pong(ctx context.Context) + write(messageType int, messageBytes []byte) + run() + read(ctx context.Context) + disconnected(err error) + createUUID() string + randomUUID() string + fireEvent(event string, data []byte, error error) +} + +type Websocket struct { + mu sync.RWMutex + // The Fiber.Websocket connection + Conn *websocket.Conn + // Define if the connection is alive or not + isAlive bool + // Queue of messages sent from the socket + queue chan message + // Channel to signal when this websocket is closed + // so go routines will stop gracefully + done chan struct{} + // Attributes map collection for the connection + attributes map[string]interface{} + // Unique id of the connection + UUID string + // Wrap Fiber Locals function + Locals func(key string) interface{} + // Wrap Fiber Params function + Params func(key string, defaultValue ...string) string + // Wrap Fiber Query function + Query func(key string, defaultValue ...string) string + // Wrap Fiber Cookies function + Cookies func(key string, defaultValue ...string) string +} + +type safePool struct { + sync.RWMutex + // List of the connections alive + conn map[string]ws +} + +// Pool with the active connections +var pool = safePool{ + conn: make(map[string]ws), +} + +func (p *safePool) set(ws ws) { + p.Lock() + p.conn[ws.GetUUID()] = ws + p.Unlock() +} + +func (p *safePool) all() map[string]ws { + p.RLock() + ret := make(map[string]ws, 0) + for wsUUID, kws := range p.conn { + ret[wsUUID] = kws + } + p.RUnlock() + return ret +} + +func (p *safePool) get(key string) ws { + p.RLock() + ret, ok := p.conn[key] + p.RUnlock() + if !ok { + panic("not found") + } + return ret +} + +func (p *safePool) contains(key string) bool { + p.RLock() + _, ok := p.conn[key] + p.RUnlock() + return ok +} + +func (p *safePool) delete(key string) { + p.Lock() + delete(p.conn, key) + p.Unlock() +} + +func (p *safePool) reset() { + p.Lock() + p.conn = make(map[string]ws) + p.Unlock() +} + +type safeListeners struct { + sync.RWMutex + list map[string][]eventCallback +} + +func (l *safeListeners) set(event string, callback eventCallback) { + l.Lock() + listeners.list[event] = append(listeners.list[event], callback) + l.Unlock() +} + +func (l *safeListeners) get(event string) []eventCallback { + l.RLock() + defer l.RUnlock() + if _, ok := l.list[event]; !ok { + return make([]eventCallback, 0) + } + + ret := make([]eventCallback, 0) + for _, v := range l.list[event] { + ret = append(ret, v) + } + return ret +} + +// List of the listeners for the events +var listeners = safeListeners{ + list: make(map[string][]eventCallback), +} + +func New(callback func(kws *Websocket)) func(*fiber.Ctx) error { + return websocket.New(func(c *websocket.Conn) { + kws := &Websocket{ + Conn: c, + Locals: func(key string) interface{} { + return c.Locals(key) + }, + Params: func(key string, defaultValue ...string) string { + return c.Params(key, defaultValue...) + }, + Query: func(key string, defaultValue ...string) string { + return c.Query(key, defaultValue...) + }, + Cookies: func(key string, defaultValue ...string) string { + return c.Cookies(key, defaultValue...) + }, + queue: make(chan message, 100), + done: make(chan struct{}, 1), + attributes: make(map[string]interface{}), + isAlive: true, + } + + // Generate uuid + kws.UUID = kws.createUUID() + + // register the connection into the pool + pool.set(kws) + + // execute the callback of the socket initialization + callback(kws) + + kws.fireEvent(EventConnect, nil, nil) + + // Run the loop for the given connection + kws.run() + }) +} + +func (kws *Websocket) GetUUID() string { + kws.mu.RLock() + defer kws.mu.RUnlock() + return kws.UUID +} + +func (kws *Websocket) SetUUID(uuid string) { + kws.mu.Lock() + defer kws.mu.Unlock() + + if pool.contains(uuid) { + panic(ErrorUUIDDuplication) + } + kws.UUID = uuid +} + +// SetAttribute Set a specific attribute for the specific socket connection +func (kws *Websocket) SetAttribute(key string, attribute interface{}) { + kws.mu.Lock() + defer kws.mu.Unlock() + kws.attributes[key] = attribute +} + +// GetAttribute Get a specific attribute from the socket attributes +func (kws *Websocket) GetAttribute(key string) interface{} { + kws.mu.RLock() + defer kws.mu.RUnlock() + value, ok := kws.attributes[key] + if ok { + return value + } + return nil +} + +// GetIntAttribute Convenience method to retrieve an attribute as an int. +// Will panic if attribute is not an int. +func (kws *Websocket) GetIntAttribute(key string) int { + kws.mu.RLock() + defer kws.mu.RUnlock() + value, ok := kws.attributes[key] + if ok { + return value.(int) + } + return 0 +} + +// GetStringAttribute Convenience method to retrieve an attribute as a string. +// Will panic if attribute is not an int. +func (kws *Websocket) GetStringAttribute(key string) string { + kws.mu.RLock() + defer kws.mu.RUnlock() + value, ok := kws.attributes[key] + if ok { + return value.(string) + } + return "" +} + +// EmitToList Emit the message to a specific socket uuids list +func (kws *Websocket) EmitToList(uuids []string, message []byte, mType ...int) { + for _, wsUUID := range uuids { + err := kws.EmitTo(wsUUID, message, mType...) + if err != nil { + kws.fireEvent(EventError, message, err) + } + } +} + +// EmitToList Emit the message to a specific socket uuids list +// Ignores all errors +func EmitToList(uuids []string, message []byte, mType ...int) { + for _, wsUUID := range uuids { + _ = EmitTo(wsUUID, message, mType...) + } +} + +// EmitTo Emit to a specific socket connection +func (kws *Websocket) EmitTo(uuid string, message []byte, mType ...int) error { + + if !pool.contains(uuid) || !pool.get(uuid).IsAlive() { + kws.fireEvent(EventError, []byte(uuid), ErrorInvalidConnection) + return ErrorInvalidConnection + } + + pool.get(uuid).Emit(message, mType...) + return nil +} + +// EmitTo Emit to a specific socket connection +func EmitTo(uuid string, message []byte, mType ...int) error { + if !pool.contains(uuid) || !pool.get(uuid).IsAlive() { + return ErrorInvalidConnection + } + + pool.get(uuid).Emit(message, mType...) + return nil +} + +// Broadcast to all the active connections +// except avoid broadcasting the message to itself +func (kws *Websocket) Broadcast(message []byte, except bool, mType ...int) { + for wsUUID := range pool.all() { + if except && kws.UUID == wsUUID { + continue + } + err := kws.EmitTo(wsUUID, message, mType...) + if err != nil { + kws.fireEvent(EventError, message, err) + } + } +} + +// Broadcast to all the active connections +func Broadcast(message []byte, mType ...int) { + for _, kws := range pool.all() { + kws.Emit(message, mType...) + } +} + +// Fire custom event +func (kws *Websocket) Fire(event string, data []byte) { + kws.fireEvent(event, data, nil) +} + +// Fire custom event on all connections +func Fire(event string, data []byte) { + fireGlobalEvent(event, data, nil) +} + +// Emit /Write the message into the given connection +func (kws *Websocket) Emit(message []byte, mType ...int) { + t := TextMessage + if len(mType) > 0 { + t = mType[0] + } + kws.write(t, message) +} + +// Close Actively close the connection from the server +func (kws *Websocket) Close() { + kws.write(CloseMessage, []byte("Connection closed")) + kws.fireEvent(EventClose, nil, nil) +} + +func (kws *Websocket) IsAlive() bool { + kws.mu.RLock() + defer kws.mu.RUnlock() + return kws.isAlive +} + +func (kws *Websocket) hasConn() bool { + kws.mu.RLock() + defer kws.mu.RUnlock() + return kws.Conn.Conn != nil +} + +func (kws *Websocket) setAlive(alive bool) { + kws.mu.Lock() + defer kws.mu.Unlock() + kws.isAlive = alive +} + +func (kws *Websocket) queueLength() int { + kws.mu.RLock() + defer kws.mu.RUnlock() + return len(kws.queue) +} + +// pong writes a control message to the client +func (kws *Websocket) pong(ctx context.Context) { + timeoutTicker := time.NewTicker(PongTimeout) + defer timeoutTicker.Stop() + for { + select { + case <-timeoutTicker.C: + kws.write(PongMessage, []byte{}) + case <-ctx.Done(): + return + } + } +} + +// Add in message queue +func (kws *Websocket) write(messageType int, messageBytes []byte) { + kws.queue <- message{ + mType: messageType, + data: messageBytes, + retries: 0, + } +} + +// Send out message queue +func (kws *Websocket) send(ctx context.Context) { + for { + select { + case message := <-kws.queue: + if !kws.hasConn() { + if message.retries <= MaxSendRetry { + // retry without blocking the sending thread + go func() { + time.Sleep(RetrySendTimeout) + message.retries = message.retries + 1 + kws.queue <- message + }() + } + continue + } + + kws.mu.RLock() + err := kws.Conn.WriteMessage(message.mType, message.data) + kws.mu.RUnlock() + + if err != nil { + kws.disconnected(err) + } + case <-ctx.Done(): + return + } + } +} + +// Start Pong/Read/Write functions +// +// Needs to be blocking, otherwise the connection would close. +func (kws *Websocket) run() { + ctx, cancelFunc := context.WithCancel(context.Background()) + + go kws.pong(ctx) + go kws.read(ctx) + go kws.send(ctx) + + <-kws.done // block until one event is sent to the done channel + + cancelFunc() +} + +// Listen for incoming messages +// and filter by message type +func (kws *Websocket) read(ctx context.Context) { + timeoutTicker := time.NewTicker(ReadTimeout) + defer timeoutTicker.Stop() + for { + select { + case <-timeoutTicker.C: + if !kws.hasConn() { + continue + } + + kws.mu.RLock() + mType, msg, err := kws.Conn.ReadMessage() + kws.mu.RUnlock() + + if mType == PingMessage { + kws.fireEvent(EventPing, nil, nil) + continue + } + + if mType == PongMessage { + kws.fireEvent(EventPong, nil, nil) + continue + } + + if mType == CloseMessage { + kws.disconnected(nil) + return + } + + if err != nil { + kws.disconnected(err) + return + } + + // We have a message and we fire the message event + kws.fireEvent(EventMessage, msg, nil) + case <-ctx.Done(): + return + } + } +} + +// When the connection closes, disconnected method +func (kws *Websocket) disconnected(err error) { + kws.fireEvent(EventDisconnect, nil, err) + + // may be called multiple times from different go routines + if kws.IsAlive() { + close(kws.done) + } + kws.setAlive(false) + + // Fire error event if the connection is + // disconnected by an error + if err != nil { + kws.fireEvent(EventError, nil, err) + } + + // Remove the socket from the pool + pool.delete(kws.UUID) +} + +// Create random UUID for each connection +func (kws *Websocket) createUUID() string { + return kws.randomUUID() +} + +// Generate random UUID. +func (kws *Websocket) randomUUID() string { + return uuid.New().String() +} + +// Fires event on all connections. +func fireGlobalEvent(event string, data []byte, error error) { + for _, kws := range pool.all() { + kws.fireEvent(event, data, error) + } +} + +// Checks if there is at least a listener for a given event +// and loop over the callbacks registered +func (kws *Websocket) fireEvent(event string, data []byte, error error) { + callbacks := listeners.get(event) + + for _, callback := range callbacks { + callback(&EventPayload{ + Kws: kws, + Name: event, + SocketUUID: kws.UUID, + SocketAttributes: kws.attributes, + Data: data, + Error: error, + }) + } +} + +type eventCallback func(payload *EventPayload) + +// On Add listener callback for an event into the listeners list +func On(event string, callback eventCallback) { + listeners.set(event, callback) +} diff --git a/ikisocket/ikisocket_test.go b/ikisocket/ikisocket_test.go new file mode 100644 index 00000000..694cae20 --- /dev/null +++ b/ikisocket/ikisocket_test.go @@ -0,0 +1,426 @@ +package ikisocket + +import ( + "context" + "net" + "strconv" + "sync" + "testing" + "time" + + "github.com/fasthttp/websocket" + fws "github.com/gofiber/contrib/websocket" + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/valyala/fasthttp/fasthttputil" +) + +const numTestConn = 10 +const numParallelTestConn = 5_000 + +type HandlerMock struct { + mock.Mock + wg sync.WaitGroup +} + +type WebsocketMock struct { + mock.Mock + mu sync.RWMutex + wg sync.WaitGroup + Conn *websocket.Conn + isAlive bool + queue map[string]message + attributes map[string]string + UUID string + Locals func(key string) interface{} + Params func(key string, defaultValue ...string) string + Query func(key string, defaultValue ...string) string + Cookies func(key string, defaultValue ...string) string +} + +func (s *WebsocketMock) SetUUID(uuid string) { + + s.mu.Lock() + defer s.mu.Unlock() + + if pool.contains(uuid) { + panic(ErrorUUIDDuplication) + } + s.UUID = uuid +} + +func (s *WebsocketMock) GetIntAttribute(key string) int { + s.mu.RLock() + defer s.mu.RUnlock() + value, ok := s.attributes[key] + if ok { + if intValue, err := strconv.Atoi(value); err == nil { + return intValue + } + } + return 0 +} + +func (s *WebsocketMock) GetStringAttribute(key string) string { + s.mu.RLock() + defer s.mu.RUnlock() + value, ok := s.attributes[key] + if ok { + return value + } + return "" +} + +func (h *HandlerMock) OnCustomEvent(payload *EventPayload) { + h.Called(payload) + h.wg.Done() +} + +func (s *WebsocketMock) Emit(message []byte, _ ...int) { + s.Called(message) + s.wg.Done() +} + +func (s *WebsocketMock) IsAlive() bool { + args := s.Called() + return args.Bool(0) +} + +func (s *WebsocketMock) GetUUID() string { + return s.UUID +} + +func TestParallelConnections(t *testing.T) { + pool.reset() + + // create test server + cfg := fiber.Config{ + DisableStartupMessage: true, + } + app := fiber.New(cfg) + ln := fasthttputil.NewInmemoryListener() + wg := sync.WaitGroup{} + + defer func() { + _ = app.Shutdown() + _ = ln.Close() + }() + + // attach upgrade middleware + app.Use(upgradeMiddleware) + + // send back response on correct message + On(EventMessage, func(payload *EventPayload) { + if string(payload.Data) == "test" { + payload.Kws.Emit([]byte("response")) + } + }) + + // create websocket endpoint + app.Get("/", New(func(kws *Websocket) { + })) + + // start server + go func() { + _ = app.Listener(ln) + }() + + wsURL := "ws://" + ln.Addr().String() + + // create concurrent connections + for i := 0; i < numParallelTestConn; i++ { + wg.Add(1) + go func() { + dialer := &websocket.Dialer{ + NetDial: func(network, addr string) (net.Conn, error) { + return ln.Dial() + }, + HandshakeTimeout: 45 * time.Second, + } + dial, _, err := dialer.Dial(wsURL, nil) + if err != nil { + t.Error(err) + return + } + + if err := dial.WriteMessage(websocket.TextMessage, []byte("test")); err != nil { + t.Error(err) + return + } + + tp, m, err := dial.ReadMessage() + if err != nil { + t.Error(err) + return + } + require.Equal(t, TextMessage, tp) + require.Equal(t, "response", string(m)) + wg.Done() + + if err := dial.Close(); err != nil { + t.Error(err) + return + } + }() + } + wg.Wait() +} + +func TestGlobalFire(t *testing.T) { + pool.reset() + + // simulate connections + for i := 0; i < numTestConn; i++ { + kws := createWS() + pool.set(kws) + } + + h := new(HandlerMock) + // setup expectations + h.On("OnCustomEvent", mock.Anything).Return(nil) + + // Moved before registration of the event + // if after can cause: panic: sync: negative WaitGroup counter + h.wg.Add(numTestConn) + + // register custom event handler + On("customevent", h.OnCustomEvent) + + // fire global custom event on all connections + Fire("customevent", []byte("test")) + + h.wg.Wait() + + h.AssertNumberOfCalls(t, "OnCustomEvent", numTestConn) +} + +func TestGlobalBroadcast(t *testing.T) { + pool.reset() + + for i := 0; i < numParallelTestConn; i++ { + mws := new(WebsocketMock) + mws.SetUUID(mws.createUUID()) + pool.set(mws) + + // setup expectations + mws.On("Emit", mock.Anything).Return(nil) + + mws.wg.Add(1) + } + + // send global broadcast to all connections + Broadcast([]byte("test"), TextMessage) + + for _, mws := range pool.all() { + mws.(*WebsocketMock).wg.Wait() + mws.(*WebsocketMock).AssertNumberOfCalls(t, "Emit", 1) + } + +} + +func TestGlobalEmitTo(t *testing.T) { + pool.reset() + + aliveUUID := "80a80sdf809dsf" + closedUUID := "las3dfj09808" + + alive := new(WebsocketMock) + alive.UUID = aliveUUID + pool.set(alive) + + closed := new(WebsocketMock) + closed.UUID = closedUUID + pool.set(closed) + + // setup expectations + alive.On("Emit", mock.Anything).Return(nil) + alive.On("IsAlive").Return(true) + closed.On("IsAlive").Return(false) + + var err error + err = EmitTo("non-existent", []byte("error")) + require.Equal(t, ErrorInvalidConnection, err) + + err = EmitTo(closedUUID, []byte("error")) + require.Equal(t, ErrorInvalidConnection, err) + + alive.wg.Add(1) + + // send global broadcast to all connections + err = EmitTo(aliveUUID, []byte("test")) + require.Nil(t, err) + + alive.wg.Wait() + + alive.AssertNumberOfCalls(t, "Emit", 1) +} + +func TestGlobalEmitToList(t *testing.T) { + pool.reset() + + uuids := []string{ + "80a80sdf809dsf", + "las3dfj09808", + } + + for _, id := range uuids { + kws := new(WebsocketMock) + kws.SetUUID(id) + kws.On("Emit", mock.Anything).Return(nil) + kws.On("IsAlive").Return(true) + kws.wg.Add(1) + pool.set(kws) + } + + // send global broadcast to all connections + EmitToList(uuids, []byte("test"), TextMessage) + + for _, kws := range pool.all() { + kws.(*WebsocketMock).wg.Wait() + kws.(*WebsocketMock).AssertNumberOfCalls(t, "Emit", 1) + } +} + +func TestWebsocket_GetIntAttribute(t *testing.T) { + kws := &Websocket{ + attributes: make(map[string]interface{}), + } + + // get unset attribute + // Will return null without panicking + + // get non-int attribute + // Will return 0 without panicking + kws.SetAttribute("notInt", "") + + // get int attribute + kws.SetAttribute("int", 3) + v := kws.GetIntAttribute("int") + require.Equal(t, 3, v) +} + +func TestWebsocket_GetStringAttribute(t *testing.T) { + kws := &Websocket{ + attributes: make(map[string]interface{}), + } + + // get unset attribute + + // get non-string attribute + kws.SetAttribute("notString", 3) + + // get string attribute + kws.SetAttribute("str", "3") + v := kws.GetStringAttribute("str") + require.Equal(t, "3", v) +} + +func assertPanic(t *testing.T, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("The code did not panic") + } + }() + f() +} + +func createWS() *Websocket { + kws := &Websocket{ + Conn: nil, + Locals: func(key string) interface{} { + return "" + }, + Params: func(key string, defaultValue ...string) string { + return "" + }, + Query: func(key string, defaultValue ...string) string { + return "" + }, + Cookies: func(key string, defaultValue ...string) string { + return "" + }, + queue: make(chan message), + attributes: make(map[string]interface{}), + isAlive: true, + } + + kws.UUID = kws.createUUID() + + return kws +} + +func upgradeMiddleware(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if fws.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired +} + +// +// needed but not used +// + +func (s *WebsocketMock) SetAttribute(_ string, _ interface{}) { + panic("implement me") +} + +func (s *WebsocketMock) GetAttribute(_ string) interface{} { + panic("implement me") +} + +func (s *WebsocketMock) EmitToList(_ []string, _ []byte, _ ...int) { + panic("implement me") +} + +func (s *WebsocketMock) EmitTo(_ string, _ []byte, _ ...int) error { + panic("implement me") +} + +func (s *WebsocketMock) Broadcast(_ []byte, _ bool, _ ...int) { + panic("implement me") +} + +func (s *WebsocketMock) Fire(_ string, _ []byte) { + panic("implement me") +} + +func (s *WebsocketMock) Close() { + panic("implement me") +} + +func (s *WebsocketMock) pong(_ context.Context) { + panic("implement me") +} + +func (s *WebsocketMock) write(_ int, _ []byte) { + panic("implement me") +} + +func (s *WebsocketMock) run() { + panic("implement me") +} + +func (s *WebsocketMock) read(_ context.Context) { + panic("implement me") +} + +func (s *WebsocketMock) disconnected(_ error) { + panic("implement me") +} + +func (s *WebsocketMock) createUUID() string { + return s.randomUUID() +} + +func (s *WebsocketMock) randomUUID() string { + return uuid.New().String() +} + +func (s *WebsocketMock) fireEvent(_ string, _ []byte, _ error) { + panic("implement me") +} From c7fadb1449cad44af9e5a145b66e30a4e1e6334e Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 13:24:03 +0200 Subject: [PATCH 02/18] added flows --- .github/dependabot.yml | 6 +++ .github/release-drafter-ikisocket.yml | 50 +++++++++++++++++++ .../workflows/release-drafter-ikisocket.yml | 19 +++++++ .github/workflows/test-ikisocket.yml | 33 ++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 .github/release-drafter-ikisocket.yml create mode 100644 .github/workflows/release-drafter-ikisocket.yml create mode 100644 .github/workflows/test-ikisocket.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5e3d4389..90eab6a5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -80,6 +80,12 @@ updates: - "๐Ÿค– Dependencies" schedule: interval: "daily" + - package-ecosystem: "gomod" + directory: "/ikisocket" # Location of package manifests + labels: + - "๐Ÿค– Dependencies" + schedule: + interval: "daily" - package-ecosystem: "gomod" directory: "/fibersentry" # Location of package manifests labels: diff --git a/.github/release-drafter-ikisocket.yml b/.github/release-drafter-ikisocket.yml new file mode 100644 index 00000000..ab1e51ea --- /dev/null +++ b/.github/release-drafter-ikisocket.yml @@ -0,0 +1,50 @@ +name-template: "Ikisocket - v$RESOLVED_VERSION" +tag-template: "ikisocket/v$RESOLVED_VERSION" +tag-prefix: ikisocket/v +include-paths: + - ikisocket +categories: + - title: "โ— Breaking Changes" + labels: + - "โ— BreakingChange" + - title: "๐Ÿš€ New" + labels: + - "โœ๏ธ Feature" + - title: "๐Ÿงน Updates" + labels: + - "๐Ÿงน Updates" + - "๐Ÿค– Dependencies" + - title: "๐Ÿ› Fixes" + labels: + - "โ˜ข๏ธ Bug" + - title: "๐Ÿ“š Documentation" + labels: + - "๐Ÿ“’ Documentation" +change-template: "- $TITLE (#$NUMBER)" +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +exclude-contributors: + - dependabot + - dependabot[bot] +version-resolver: + major: + labels: + - "major" + - "โ— BreakingChange" + minor: + labels: + - "minor" + - "โœ๏ธ Feature" + patch: + labels: + - "patch" + - "๐Ÿ“’ Documentation" + - "โ˜ข๏ธ Bug" + - "๐Ÿค– Dependencies" + - "๐Ÿงน Updates" + default: patch +template: | + $CHANGES + + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...websocket/v$RESOLVED_VERSION + + Thank you $CONTRIBUTORS for making this update possible. diff --git a/.github/workflows/release-drafter-ikisocket.yml b/.github/workflows/release-drafter-ikisocket.yml new file mode 100644 index 00000000..92a8b164 --- /dev/null +++ b/.github/workflows/release-drafter-ikisocket.yml @@ -0,0 +1,19 @@ +name: Release Drafter Websocket +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + - main + paths: + - 'ikisocket/**' +jobs: + draft_release_websocket: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: release-drafter/release-drafter@v5 + with: + config-name: release-drafter-ikisocket.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-ikisocket.yml b/.github/workflows/test-ikisocket.yml new file mode 100644 index 00000000..d48ab075 --- /dev/null +++ b/.github/workflows/test-ikisocket.yml @@ -0,0 +1,33 @@ +name: "Test ikisocket" + +on: + push: + branches: + - master + - main + paths: + - "ikisocket/**" + pull_request: + paths: + - "ikisocket/**" + +jobs: + Tests: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: + - 1.18.x + - 1.19.x + - 1.20.x + - 1.21.x + steps: + - name: Fetch Repository + uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: "${{ matrix.go-version }}" + - name: Run Test + working-directory: ./ikisocket + run: go test -v -race ./... From b54c35ac2d17e8e3f8c5193ba01060dbf3b10f87 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 13:55:42 +0200 Subject: [PATCH 03/18] Readme improvements --- README.md | 1 + ikisocket/README.md | 139 ++++++++++---------------------------------- 2 files changed, 32 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index 3a4790b3..a60084b6 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Repository for third party middlewares with dependencies. * [Fibersentry](./fibersentry/README.md) * [Fiberzap](./fiberzap/README.md) * [Fiberzerolog](./fiberzerolog/README.md) +* [Ikisocket](./ikisocket/README.md) * [JWT](./jwt/README.md) * [Loadshed](./loadshed/README.md) * [NewRelic](./fibernewrelic/README.md) diff --git a/ikisocket/README.md b/ikisocket/README.md index 29e289eb..f0064faa 100644 --- a/ikisocket/README.md +++ b/ikisocket/README.md @@ -57,7 +57,6 @@ func Broadcast(message []byte) func Fire(event string, data []byte) ``` - ## Example ```go @@ -194,122 +193,46 @@ func main() { --- - ## Supported events -```go -// Supported event list -const ( - // Fired when a Text/Binary message is received - EventMessage = "message" - // More details here: - // @url https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets - EventPing = "ping" - EventPong = "pong" - // Fired on disconnection - // The error provided in disconnection event - // is defined in RFC 6455, section 11.7. - // @url https://github.com/gofiber/websocket/blob/cd4720c435de415b864d975a9ca23a47eaf081ef/websocket.go#L192 - EventDisconnect = "disconnect" - // Fired on first connection - EventConnect = "connect" - // Fired when the connection is actively closed from the server - EventClose = "close" - // Fired when some error appears useful also for debugging websockets - EventError = "error" -) -``` +| Const | Event | Description | +|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| +| EventMessage| `message`|Fired when a Text/Binary message is received| +| EventPing| `ping`| More details here: @url | +| EventPong| `pong`| Refer to ping description| +| EventDisconnect| `disconnect`| Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7.| +| EventConnect | `connect`| Fired on first connection| +| EventClose | `close`|Fired when the connection is actively closed from the server. Different from client disconnection | +| EventError | `error`| Fired when some error appears useful also for debugging websockets| ## Event Payload object -```go -// Event Payload is the object that -// stores all the information about the event and -// the connection -type EventPayload struct { - // The connection object - Kws *Websocket - // The name of the event - Name string - // Unique connection UUID - SocketUUID string - // Optional websocket attributes - SocketAttributes map[string]string - // Optional error when are fired events like - // - Disconnect - // - Error - Error error - // Data is used on Message and on Error event - Data []byte -} -``` - ---- - -**The FastHTTP connection can be accessed directly from the struct** - -```go -type Websocket struct { - // The FastHTTP connection - Conn *websocket.Conn -} -``` - -Can be accessed from - -```go -kws.Conn -``` +| Variable | Type | Description | +|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| +| Kws| `*Websocket`|The connection object| +| Name|`string`|The name of the event| +| SocketUUID| `string`|Unique connection UUID| +| SocketAttributes| `map[string]string`| Optional websocket attributes| +| Error | `error`|(optional) Fired from disconnection or error events| +| Data | `[]byte`|Data used on Message and on Error event, contains the payload for custom events| ## Socket instance functions -```go -// Set a specific attribute for the specific socket connection -func (kws *Websocket) SetAttribute(key string, attribute string) -``` +| Name | Type | Description | +|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| +| SetAttribute| `void`|Set a specific attribute for the specific socket connection| +| GetUUID|`string`|Get socket connection UUID| +| SetUUID|`void`|Set socket connection UUID| +| GetAttribute|`string`|Get a specific attribute from the socket attributes| +| EmitToList|`void`|Emit the message to a specific socket uuids list| +| EmitTo|`error`|Emit to a specific socket connection| +| Broadcast|`void`|Broadcast to all the active connections except broadcasting the message to itself| +| Fire|`void`| Fire custom event| +| Emit|`void`|Emit/Write the message into the given connection| +| Close|`void`|Actively close the connection from the server| -```go -// Get socket connection UUID -func (kws *Websocket) GetUUID() string -``` +**Note: the FastHTTP connection can be accessed directly from the instance** ```go -// Set socket connection UUID -func (kws *Websocket) SetUUID(uuid string) -``` - -```go -// Get a specific attribute from the socket attributes -func (kws *Websocket) GetAttribute(key string) string -``` - -```go -// Emit the message to a specific socket uuids list -func (kws *Websocket) EmitToList(uuids []string, message []byte) -``` - -```go -// Emit to a specific socket connection -func (kws *Websocket) EmitTo(uuid string, message []byte) error -``` - -```go -// Broadcast to all the active connections -// except avoid broadcasting the message to itself -func (kws *Websocket) Broadcast(message []byte, except bool) -``` - -```go -// Fire custom event -func (kws *Websocket) Fire(event string, data []byte) -``` - -```go -// Emit/Write the message into the given connection -func (kws *Websocket) Emit(message []byte) +kws.Conn ``` - -```go -// Actively close the connection from the server -func (kws *Websocket) Close() -``` \ No newline at end of file From 0748255db2078de0d71fca33f852c2d85490958d Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 13:57:15 +0200 Subject: [PATCH 04/18] typo fix --- ikisocket/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/ikisocket/README.md b/ikisocket/README.md index f0064faa..d741368c 100644 --- a/ikisocket/README.md +++ b/ikisocket/README.md @@ -1,6 +1,3 @@ - ---- - id: ikisocket --- From 59be3a63561b7baf68a080cfc1c46df2f115dfc3 Mon Sep 17 00:00:00 2001 From: antoniodipinto Date: Fri, 9 Feb 2024 14:05:31 +0200 Subject: [PATCH 05/18] Update .github/release-drafter-ikisocket.yml Co-authored-by: RW --- .github/release-drafter-ikisocket.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter-ikisocket.yml b/.github/release-drafter-ikisocket.yml index ab1e51ea..effb54a2 100644 --- a/.github/release-drafter-ikisocket.yml +++ b/.github/release-drafter-ikisocket.yml @@ -45,6 +45,6 @@ version-resolver: template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...websocket/v$RESOLVED_VERSION + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...ikisocket/v$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. From 4fb0a2f95b3d91c3d12f6e75c00d6d256fce47c7 Mon Sep 17 00:00:00 2001 From: antoniodipinto Date: Fri, 9 Feb 2024 14:05:43 +0200 Subject: [PATCH 06/18] Update .github/workflows/release-drafter-ikisocket.yml Co-authored-by: RW --- .github/workflows/release-drafter-ikisocket.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter-ikisocket.yml b/.github/workflows/release-drafter-ikisocket.yml index 92a8b164..eb49a6ac 100644 --- a/.github/workflows/release-drafter-ikisocket.yml +++ b/.github/workflows/release-drafter-ikisocket.yml @@ -1,4 +1,4 @@ -name: Release Drafter Websocket +name: Release Drafter ikisocket on: push: # branches to consider in the event; optional, defaults to all From 2601b5ecdc12697c2e890e8c6aff3f516c274753 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 9 Feb 2024 13:11:10 +0100 Subject: [PATCH 07/18] Update README.md correct indentation --- ikisocket/README.md | 295 ++++++++++++++++++++++---------------------- 1 file changed, 148 insertions(+), 147 deletions(-) diff --git a/ikisocket/README.md b/ikisocket/README.md index d741368c..801ae14b 100644 --- a/ikisocket/README.md +++ b/ikisocket/README.md @@ -8,7 +8,8 @@ id: ikisocket ![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) ![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) -WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired by [Socket.io](https://github.com/socketio/socket.io) +WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired +by [Socket.io](https://github.com/socketio/socket.io) **Note: Requires Go 1.18 and above** @@ -24,12 +25,12 @@ go get -u github.com/gofiber/contrib/ikisocket ```go // Initialize new ikisocket in the callback this will // execute a callback that expects kws *Websocket Object -func New(callback func(kws *Websocket)) func(*fiber.Ctx) error +func New(callback func (kws *Websocket)) func (*fiber.Ctx) error ``` ```go // Add listener callback for an event into the listeners list -func On(event string, callback func(payload *EventPayload)) +func On(event string, callback func (payload *EventPayload)) ``` ```go @@ -60,130 +61,130 @@ func Fire(event string, data []byte) package main import ( - "encoding/json" - "fmt" - "log" + "encoding/json" + "fmt" + "log" - "github.com/gofiber/contrib/ikisocket" - "github.com/gofiber/contrib/websocket" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/ikisocket" + "github.com/gofiber/contrib/websocket" + "github.com/gofiber/fiber/v2" ) // MessageObject Basic chat message object type MessageObject struct { - Data string `json:"data"` - From string `json:"from"` - Event string `json:"event"` - To string `json:"to"` + Data string `json:"data"` + From string `json:"from"` + Event string `json:"event"` + To string `json:"to"` } func main() { - // The key for the map is message.to - clients := make(map[string]string) - - // Start a new Fiber application - app := fiber.New() - - // Setup the middleware to retrieve the data sent in first GET request - app.Use(func(c *fiber.Ctx) error { - // IsWebSocketUpgrade returns true if the client - // requested upgrade to the WebSocket protocol. - if websocket.IsWebSocketUpgrade(c) { - c.Locals("allowed", true) - return c.Next() - } - return fiber.ErrUpgradeRequired - }) - - // Multiple event handling supported - ikisocket.On(ikisocket.EventConnect, func(ep *ikisocket.EventPayload) { - fmt.Println(fmt.Sprintf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))) - }) - - // Custom event handling supported - ikisocket.On("CUSTOM_EVENT", func(ep *ikisocket.EventPayload) { - fmt.Println(fmt.Sprintf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))) - // ---> - - // DO YOUR BUSINESS HERE - - // ---> - }) - - // On message event - ikisocket.On(ikisocket.EventMessage, func(ep *ikisocket.EventPayload) { - - fmt.Println(fmt.Sprintf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))) - - message := MessageObject{} - - // Unmarshal the json message - // { - // "from": "", - // "to": "", - // "event": "CUSTOM_EVENT", - // "data": "hello" - //} - err := json.Unmarshal(ep.Data, &message) - if err != nil { - fmt.Println(err) - return - } - - // Fire custom event based on some - // business logic - if message.Event != "" { - ep.Kws.Fire(message.Event, []byte(message.Data)) - } - - // Emit the message directly to specified user - err = ep.Kws.EmitTo(clients[message.To], ep.Data, ikisocket.TextMessage) - if err != nil { - fmt.Println(err) - } - }) - - // On disconnect event - ikisocket.On(ikisocket.EventDisconnect, func(ep *ikisocket.EventPayload) { - // Remove the user from the local clients - delete(clients, ep.Kws.GetStringAttribute("user_id")) - fmt.Println(fmt.Sprintf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))) - }) - - // On close event - // This event is called when the server disconnects the user actively with .Close() method - ikisocket.On(ikisocket.EventClose, func(ep *ikisocket.EventPayload) { - // Remove the user from the local clients - delete(clients, ep.Kws.GetStringAttribute("user_id")) - fmt.Println(fmt.Sprintf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))) - }) - - // On error event - ikisocket.On(ikisocket.EventError, func(ep *ikisocket.EventPayload) { - fmt.Println(fmt.Sprintf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))) - }) - - app.Get("/ws/:id", ikisocket.New(func(kws *ikisocket.Websocket) { - - // Retrieve the user id from endpoint - userId := kws.Params("id") - - // Add the connection to the list of the connected clients - // The UUID is generated randomly and is the key that allow - // ikisocket to manage Emit/EmitTo/Broadcast - clients[userId] = kws.UUID - - // Every websocket connection has an optional session key => value storage - kws.SetAttribute("user_id", userId) - - //Broadcast to all the connected users the newcomer - kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, ikisocket.TextMessage) - //Write welcome message - kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), ikisocket.TextMessage) - })) - - log.Fatal(app.Listen(":3000")) + // The key for the map is message.to + clients := make(map[string]string) + + // Start a new Fiber application + app := fiber.New() + + // Setup the middleware to retrieve the data sent in first GET request + app.Use(func(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + // Multiple event handling supported + ikisocket.On(ikisocket.EventConnect, func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // Custom event handling supported + ikisocket.On("CUSTOM_EVENT", func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + // ---> + + // DO YOUR BUSINESS HERE + + // ---> + }) + + // On message event + ikisocket.On(ikisocket.EventMessage, func(ep *ikisocket.EventPayload) { + + fmt.Println(fmt.Sprintf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))) + + message := MessageObject{} + + // Unmarshal the json message + // { + // "from": "", + // "to": "", + // "event": "CUSTOM_EVENT", + // "data": "hello" + //} + err := json.Unmarshal(ep.Data, &message) + if err != nil { + fmt.Println(err) + return + } + + // Fire custom event based on some + // business logic + if message.Event != "" { + ep.Kws.Fire(message.Event, []byte(message.Data)) + } + + // Emit the message directly to specified user + err = ep.Kws.EmitTo(clients[message.To], ep.Data, ikisocket.TextMessage) + if err != nil { + fmt.Println(err) + } + }) + + // On disconnect event + ikisocket.On(ikisocket.EventDisconnect, func(ep *ikisocket.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On close event + // This event is called when the server disconnects the user actively with .Close() method + ikisocket.On(ikisocket.EventClose, func(ep *ikisocket.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On error event + ikisocket.On(ikisocket.EventError, func(ep *ikisocket.EventPayload) { + fmt.Println(fmt.Sprintf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + app.Get("/ws/:id", ikisocket.New(func(kws *ikisocket.Websocket) { + + // Retrieve the user id from endpoint + userId := kws.Params("id") + + // Add the connection to the list of the connected clients + // The UUID is generated randomly and is the key that allow + // ikisocket to manage Emit/EmitTo/Broadcast + clients[userId] = kws.UUID + + // Every websocket connection has an optional session key => value storage + kws.SetAttribute("user_id", userId) + + //Broadcast to all the connected users the newcomer + kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, ikisocket.TextMessage) + //Write welcome message + kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), ikisocket.TextMessage) + })) + + log.Fatal(app.Listen(":3000")) } ``` @@ -192,41 +193,41 @@ func main() { ## Supported events -| Const | Event | Description | -|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| -| EventMessage| `message`|Fired when a Text/Binary message is received| -| EventPing| `ping`| More details here: @url | -| EventPong| `pong`| Refer to ping description| -| EventDisconnect| `disconnect`| Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7.| -| EventConnect | `connect`| Fired on first connection| -| EventClose | `close`|Fired when the connection is actively closed from the server. Different from client disconnection | -| EventError | `error`| Fired when some error appears useful also for debugging websockets| +| Const | Event | Description | +|:----------------|:-------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| EventMessage | `message` | Fired when a Text/Binary message is received | +| EventPing | `ping` | More details here: @url | +| EventPong | `pong` | Refer to ping description | +| EventDisconnect | `disconnect` | Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7. | +| EventConnect | `connect` | Fired on first connection | +| EventClose | `close` | Fired when the connection is actively closed from the server. Different from client disconnection | +| EventError | `error` | Fired when some error appears useful also for debugging websockets | ## Event Payload object -| Variable | Type | Description | -|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| -| Kws| `*Websocket`|The connection object| -| Name|`string`|The name of the event| -| SocketUUID| `string`|Unique connection UUID| -| SocketAttributes| `map[string]string`| Optional websocket attributes| -| Error | `error`|(optional) Fired from disconnection or error events| -| Data | `[]byte`|Data used on Message and on Error event, contains the payload for custom events| +| Variable | Type | Description | +|:-----------------|:--------------------|:--------------------------------------------------------------------------------| +| Kws | `*Websocket` | The connection object | +| Name | `string` | The name of the event | +| SocketUUID | `string` | Unique connection UUID | +| SocketAttributes | `map[string]string` | Optional websocket attributes | +| Error | `error` | (optional) Fired from disconnection or error events | +| Data | `[]byte` | Data used on Message and on Error event, contains the payload for custom events | ## Socket instance functions -| Name | Type | Description | -|:--------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------------------------------| -| SetAttribute| `void`|Set a specific attribute for the specific socket connection| -| GetUUID|`string`|Get socket connection UUID| -| SetUUID|`void`|Set socket connection UUID| -| GetAttribute|`string`|Get a specific attribute from the socket attributes| -| EmitToList|`void`|Emit the message to a specific socket uuids list| -| EmitTo|`error`|Emit to a specific socket connection| -| Broadcast|`void`|Broadcast to all the active connections except broadcasting the message to itself| -| Fire|`void`| Fire custom event| -| Emit|`void`|Emit/Write the message into the given connection| -| Close|`void`|Actively close the connection from the server| +| Name | Type | Description | +|:-------------|:---------|:----------------------------------------------------------------------------------| +| SetAttribute | `void` | Set a specific attribute for the specific socket connection | +| GetUUID | `string` | Get socket connection UUID | +| SetUUID | `void` | Set socket connection UUID | +| GetAttribute | `string` | Get a specific attribute from the socket attributes | +| EmitToList | `void` | Emit the message to a specific socket uuids list | +| EmitTo | `error` | Emit to a specific socket connection | +| Broadcast | `void` | Broadcast to all the active connections except broadcasting the message to itself | +| Fire | `void` | Fire custom event | +| Emit | `void` | Emit/Write the message into the given connection | +| Close | `void` | Actively close the connection from the server | **Note: the FastHTTP connection can be accessed directly from the instance** From 2af0a0157a3c55fb2928bc4e66c7218846d18e1e Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 9 Feb 2024 13:12:55 +0100 Subject: [PATCH 08/18] Update README.md --- ikisocket/README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ikisocket/README.md b/ikisocket/README.md index 801ae14b..82aad877 100644 --- a/ikisocket/README.md +++ b/ikisocket/README.md @@ -8,8 +8,7 @@ id: ikisocket ![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) ![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) -WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired -by [Socket.io](https://github.com/socketio/socket.io) +WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired by [Socket.io](https://github.com/socketio/socket.io) **Note: Requires Go 1.18 and above** @@ -25,12 +24,12 @@ go get -u github.com/gofiber/contrib/ikisocket ```go // Initialize new ikisocket in the callback this will // execute a callback that expects kws *Websocket Object -func New(callback func (kws *Websocket)) func (*fiber.Ctx) error +func New(callback func(kws *Websocket)) func(*fiber.Ctx) error ``` ```go // Add listener callback for an event into the listeners list -func On(event string, callback func (payload *EventPayload)) +func On(event string, callback func(payload *EventPayload)) ``` ```go From 015e75f5c4cee5efc481fac0e034fb15d2a6308f Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 14:25:24 +0200 Subject: [PATCH 09/18] Update SocketAttributes type in EventPayload struct and tidy update --- ikisocket/go.mod | 18 +++++++++++++++- ikisocket/go.sum | 49 +----------------------------------------- ikisocket/ikisocket.go | 2 +- 3 files changed, 19 insertions(+), 50 deletions(-) diff --git a/ikisocket/go.mod b/ikisocket/go.mod index 09f457f4..f27eeb3e 100644 --- a/ikisocket/go.mod +++ b/ikisocket/go.mod @@ -7,7 +7,23 @@ require ( github.com/gofiber/contrib/websocket v1.2.0 github.com/gofiber/fiber/v2 v2.50.0 github.com/google/uuid v1.3.1 - github.com/rivo/uniseg v0.4.4 // indirect github.com/stretchr/testify v1.8.4 github.com/valyala/fasthttp v1.50.0 ) + +require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.13.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/ikisocket/go.sum b/ikisocket/go.sum index dccf4c25..e717a2fe 100644 --- a/ikisocket/go.sum +++ b/ikisocket/go.sum @@ -7,13 +7,10 @@ github.com/fasthttp/websocket v1.5.4 h1:Bq8HIcoiffh3pmwSKB8FqaNooluStLQQxnzQspMa github.com/fasthttp/websocket v1.5.4/go.mod h1:R2VXd4A6KBspb5mTrsWnZwn6ULkX56/Ktk8/0UNSJao= github.com/gofiber/contrib/websocket v1.2.0 h1:E+GNxglSApjJCPwH1y3wLz69c1PuSvADwhMBeDc8Xxc= github.com/gofiber/contrib/websocket v1.2.0/go.mod h1:Sf8RYFluiIKxONa/Kq0jk05EOUtqrb81pJopTxzcsX4= -github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -21,10 +18,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -40,59 +35,17 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/ikisocket/ikisocket.go b/ikisocket/ikisocket.go index e443b600..6423b1a3 100644 --- a/ikisocket/ikisocket.go +++ b/ikisocket/ikisocket.go @@ -91,7 +91,7 @@ type EventPayload struct { // Unique connection UUID SocketUUID string // Optional websocket attributes - SocketAttributes map[string]interface{} + SocketAttributes map[string]any // Optional error when are fired events like // - Disconnect // - Error From 4d5cdd14b9a2161b35ff0a30a233c2dfac6240c4 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 14:59:01 +0200 Subject: [PATCH 10/18] Update Go version to 1.22.x --- .github/workflows/test-ikisocket.yml | 3 +-- ikisocket/go.mod | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-ikisocket.yml b/.github/workflows/test-ikisocket.yml index d48ab075..7b05f5ff 100644 --- a/.github/workflows/test-ikisocket.yml +++ b/.github/workflows/test-ikisocket.yml @@ -17,10 +17,9 @@ jobs: strategy: matrix: go-version: - - 1.18.x - - 1.19.x - 1.20.x - 1.21.x + - 1.22.x steps: - name: Fetch Repository uses: actions/checkout@v4 diff --git a/ikisocket/go.mod b/ikisocket/go.mod index f27eeb3e..830e2e28 100644 --- a/ikisocket/go.mod +++ b/ikisocket/go.mod @@ -1,6 +1,6 @@ module github.com/antoniodipinto/ikisocket -go 1.18 +go 1.20 require ( github.com/fasthttp/websocket v1.5.4 From 6d3014b0afba25ee9d1d6e039df233d2330b79ae Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 15:02:58 +0200 Subject: [PATCH 11/18] Update module path in go.mod file --- ikisocket/go.mod | 4 ++-- ikisocket/go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ikisocket/go.mod b/ikisocket/go.mod index 830e2e28..52ddf950 100644 --- a/ikisocket/go.mod +++ b/ikisocket/go.mod @@ -1,11 +1,11 @@ -module github.com/antoniodipinto/ikisocket +module github.com/gofiber/contrib/ikisocket go 1.20 require ( github.com/fasthttp/websocket v1.5.4 github.com/gofiber/contrib/websocket v1.2.0 - github.com/gofiber/fiber/v2 v2.50.0 + github.com/gofiber/fiber/v2 v2.48.0 github.com/google/uuid v1.3.1 github.com/stretchr/testify v1.8.4 github.com/valyala/fasthttp v1.50.0 diff --git a/ikisocket/go.sum b/ikisocket/go.sum index e717a2fe..6fc1b2cf 100644 --- a/ikisocket/go.sum +++ b/ikisocket/go.sum @@ -7,8 +7,8 @@ github.com/fasthttp/websocket v1.5.4 h1:Bq8HIcoiffh3pmwSKB8FqaNooluStLQQxnzQspMa github.com/fasthttp/websocket v1.5.4/go.mod h1:R2VXd4A6KBspb5mTrsWnZwn6ULkX56/Ktk8/0UNSJao= github.com/gofiber/contrib/websocket v1.2.0 h1:E+GNxglSApjJCPwH1y3wLz69c1PuSvADwhMBeDc8Xxc= github.com/gofiber/contrib/websocket v1.2.0/go.mod h1:Sf8RYFluiIKxONa/Kq0jk05EOUtqrb81pJopTxzcsX4= -github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= -github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= +github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0= +github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= From be3f68c7967ef810ea548b81b42c00ac76a31ce9 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 15:08:35 +0200 Subject: [PATCH 12/18] Update dependencies in go.mod and go.sum files --- ikisocket/go.mod | 12 ++++++------ ikisocket/go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ikisocket/go.mod b/ikisocket/go.mod index 52ddf950..b6673a4a 100644 --- a/ikisocket/go.mod +++ b/ikisocket/go.mod @@ -5,18 +5,18 @@ go 1.20 require ( github.com/fasthttp/websocket v1.5.4 github.com/gofiber/contrib/websocket v1.2.0 - github.com/gofiber/fiber/v2 v2.48.0 - github.com/google/uuid v1.3.1 + github.com/gofiber/fiber/v2 v2.52.0 + github.com/google/uuid v1.5.0 github.com/stretchr/testify v1.8.4 - github.com/valyala/fasthttp v1.50.0 + github.com/valyala/fasthttp v1.51.0 ) require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect @@ -24,6 +24,6 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ikisocket/go.sum b/ikisocket/go.sum index 6fc1b2cf..34480385 100644 --- a/ikisocket/go.sum +++ b/ikisocket/go.sum @@ -7,17 +7,17 @@ github.com/fasthttp/websocket v1.5.4 h1:Bq8HIcoiffh3pmwSKB8FqaNooluStLQQxnzQspMa github.com/fasthttp/websocket v1.5.4/go.mod h1:R2VXd4A6KBspb5mTrsWnZwn6ULkX56/Ktk8/0UNSJao= github.com/gofiber/contrib/websocket v1.2.0 h1:E+GNxglSApjJCPwH1y3wLz69c1PuSvADwhMBeDc8Xxc= github.com/gofiber/contrib/websocket v1.2.0/go.mod h1:Sf8RYFluiIKxONa/Kq0jk05EOUtqrb81pJopTxzcsX4= -github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0= -github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= +github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -37,14 +37,14 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= -github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 13b9c159a88d096c96fa24347687da47aa1046d2 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Fri, 9 Feb 2024 15:53:56 +0200 Subject: [PATCH 13/18] Refactor event callback retrieval in safeListeners --- ikisocket/ikisocket.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ikisocket/ikisocket.go b/ikisocket/ikisocket.go index 6423b1a3..e89e8f7e 100644 --- a/ikisocket/ikisocket.go +++ b/ikisocket/ikisocket.go @@ -224,9 +224,7 @@ func (l *safeListeners) get(event string) []eventCallback { } ret := make([]eventCallback, 0) - for _, v := range l.list[event] { - ret = append(ret, v) - } + ret = append(ret, l.list[event]...) return ret } From e9d56280538d292fc34f1418572be8688b2e37fb Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Tue, 13 Feb 2024 10:46:07 +0200 Subject: [PATCH 14/18] added nolint fix for test failing --- ikisocket/ikisocket.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ikisocket/ikisocket.go b/ikisocket/ikisocket.go index e89e8f7e..a3b7f4ff 100644 --- a/ikisocket/ikisocket.go +++ b/ikisocket/ikisocket.go @@ -199,6 +199,7 @@ func (p *safePool) delete(key string) { p.Unlock() } +//nolint:all func (p *safePool) reset() { p.Lock() p.conn = make(map[string]ws) @@ -433,6 +434,7 @@ func (kws *Websocket) setAlive(alive bool) { kws.isAlive = alive } +//nolint:all func (kws *Websocket) queueLength() int { kws.mu.RLock() defer kws.mu.RUnlock() From f6067ebab3809d6b54433937ec88d2345fe3b71f Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Tue, 13 Feb 2024 16:00:03 +0200 Subject: [PATCH 15/18] removed unnecessary panic and updated doc --- ikisocket/README.md | 2 +- ikisocket/ikisocket.go | 31 ++++++++++++++++++++----------- ikisocket/ikisocket_test.go | 3 ++- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/ikisocket/README.md b/ikisocket/README.md index 82aad877..78e54595 100644 --- a/ikisocket/README.md +++ b/ikisocket/README.md @@ -219,7 +219,7 @@ func main() { |:-------------|:---------|:----------------------------------------------------------------------------------| | SetAttribute | `void` | Set a specific attribute for the specific socket connection | | GetUUID | `string` | Get socket connection UUID | -| SetUUID | `void` | Set socket connection UUID | +| SetUUID | `error` | Set socket connection UUID | | GetAttribute | `string` | Get a specific attribute from the socket attributes | | EmitToList | `void` | Emit the message to a specific socket uuids list | | EmitTo | `error` | Emit to a specific socket connection | diff --git a/ikisocket/ikisocket.go b/ikisocket/ikisocket.go index a3b7f4ff..87032493 100644 --- a/ikisocket/ikisocket.go +++ b/ikisocket/ikisocket.go @@ -103,7 +103,7 @@ type EventPayload struct { type ws interface { IsAlive() bool GetUUID() string - SetUUID(uuid string) + SetUUID(uuid string) error SetAttribute(key string, attribute interface{}) GetAttribute(key string) interface{} GetIntAttribute(key string) int @@ -176,14 +176,14 @@ func (p *safePool) all() map[string]ws { return ret } -func (p *safePool) get(key string) ws { +func (p *safePool) get(key string) (ws, error) { p.RLock() ret, ok := p.conn[key] p.RUnlock() if !ok { - panic("not found") + return nil, ErrorInvalidConnection } - return ret + return ret, nil } func (p *safePool) contains(key string) bool { @@ -278,14 +278,15 @@ func (kws *Websocket) GetUUID() string { return kws.UUID } -func (kws *Websocket) SetUUID(uuid string) { +func (kws *Websocket) SetUUID(uuid string) error { kws.mu.Lock() defer kws.mu.Unlock() if pool.contains(uuid) { - panic(ErrorUUIDDuplication) + return ErrorUUIDDuplication } kws.UUID = uuid + return nil } // SetAttribute Set a specific attribute for the specific socket connection @@ -319,7 +320,6 @@ func (kws *Websocket) GetIntAttribute(key string) int { } // GetStringAttribute Convenience method to retrieve an attribute as a string. -// Will panic if attribute is not an int. func (kws *Websocket) GetStringAttribute(key string) string { kws.mu.RLock() defer kws.mu.RUnlock() @@ -351,22 +351,31 @@ func EmitToList(uuids []string, message []byte, mType ...int) { // EmitTo Emit to a specific socket connection func (kws *Websocket) EmitTo(uuid string, message []byte, mType ...int) error { - if !pool.contains(uuid) || !pool.get(uuid).IsAlive() { + conn, err := pool.get(uuid) + if err != nil { + return err + } + if !pool.contains(uuid) || !conn.IsAlive() { kws.fireEvent(EventError, []byte(uuid), ErrorInvalidConnection) return ErrorInvalidConnection } - pool.get(uuid).Emit(message, mType...) + conn.Emit(message, mType...) return nil } // EmitTo Emit to a specific socket connection func EmitTo(uuid string, message []byte, mType ...int) error { - if !pool.contains(uuid) || !pool.get(uuid).IsAlive() { + conn, err := pool.get(uuid) + if err != nil { + return err + } + + if !pool.contains(uuid) || !conn.IsAlive() { return ErrorInvalidConnection } - pool.get(uuid).Emit(message, mType...) + conn.Emit(message, mType...) return nil } diff --git a/ikisocket/ikisocket_test.go b/ikisocket/ikisocket_test.go index 694cae20..f33cae83 100644 --- a/ikisocket/ikisocket_test.go +++ b/ikisocket/ikisocket_test.go @@ -40,7 +40,7 @@ type WebsocketMock struct { Cookies func(key string, defaultValue ...string) string } -func (s *WebsocketMock) SetUUID(uuid string) { +func (s *WebsocketMock) SetUUID(uuid string) error { s.mu.Lock() defer s.mu.Unlock() @@ -49,6 +49,7 @@ func (s *WebsocketMock) SetUUID(uuid string) { panic(ErrorUUIDDuplication) } s.UUID = uuid + return nil } func (s *WebsocketMock) GetIntAttribute(key string) int { From dc5267ca4edb5aa7d229508ebfdf1a619739c208 Mon Sep 17 00:00:00 2001 From: Antonio Di Pinto Date: Sat, 17 Feb 2024 13:50:49 +0200 Subject: [PATCH 16/18] Changed name from ikisocket to socketio --- .github/dependabot.yml | 2 +- ...ocket.yml => release-drafter-socketio.yml} | 10 +++--- ...ocket.yml => release-drafter-socketio.yml} | 6 ++-- .../{test-ikisocket.yml => test-socketio.yml} | 8 ++--- README.md | 2 +- {ikisocket => socketio}/README.md | 34 +++++++++---------- {ikisocket => socketio}/go.mod | 2 +- {ikisocket => socketio}/go.sum | 0 .../ikisocket.go => socketio/socketio.go | 2 +- .../socketio_test.go | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) rename .github/{release-drafter-ikisocket.yml => release-drafter-socketio.yml} (86%) rename .github/workflows/{release-drafter-ikisocket.yml => release-drafter-socketio.yml} (77%) rename .github/workflows/{test-ikisocket.yml => test-socketio.yml} (78%) rename {ikisocket => socketio}/README.md (90%) rename {ikisocket => socketio}/go.mod (95%) rename {ikisocket => socketio}/go.sum (100%) rename ikisocket/ikisocket.go => socketio/socketio.go (99%) rename ikisocket/ikisocket_test.go => socketio/socketio_test.go (99%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 90eab6a5..3c1c3ec3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -81,7 +81,7 @@ updates: schedule: interval: "daily" - package-ecosystem: "gomod" - directory: "/ikisocket" # Location of package manifests + directory: "/socketio" # Location of package manifests labels: - "๐Ÿค– Dependencies" schedule: diff --git a/.github/release-drafter-ikisocket.yml b/.github/release-drafter-socketio.yml similarity index 86% rename from .github/release-drafter-ikisocket.yml rename to .github/release-drafter-socketio.yml index effb54a2..473269c0 100644 --- a/.github/release-drafter-ikisocket.yml +++ b/.github/release-drafter-socketio.yml @@ -1,8 +1,8 @@ -name-template: "Ikisocket - v$RESOLVED_VERSION" -tag-template: "ikisocket/v$RESOLVED_VERSION" -tag-prefix: ikisocket/v +name-template: "socketio - v$RESOLVED_VERSION" +tag-template: "socketio/v$RESOLVED_VERSION" +tag-prefix: socketio/v include-paths: - - ikisocket + - socketio categories: - title: "โ— Breaking Changes" labels: @@ -45,6 +45,6 @@ version-resolver: template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...ikisocket/v$RESOLVED_VERSION + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...socketio/v$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. diff --git a/.github/workflows/release-drafter-ikisocket.yml b/.github/workflows/release-drafter-socketio.yml similarity index 77% rename from .github/workflows/release-drafter-ikisocket.yml rename to .github/workflows/release-drafter-socketio.yml index eb49a6ac..7005170e 100644 --- a/.github/workflows/release-drafter-ikisocket.yml +++ b/.github/workflows/release-drafter-socketio.yml @@ -1,4 +1,4 @@ -name: Release Drafter ikisocket +name: Release Drafter socketio on: push: # branches to consider in the event; optional, defaults to all @@ -6,7 +6,7 @@ on: - master - main paths: - - 'ikisocket/**' + - 'socketio/**' jobs: draft_release_websocket: runs-on: ubuntu-latest @@ -14,6 +14,6 @@ jobs: steps: - uses: release-drafter/release-drafter@v5 with: - config-name: release-drafter-ikisocket.yml + config-name: release-drafter-socketio.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test-ikisocket.yml b/.github/workflows/test-socketio.yml similarity index 78% rename from .github/workflows/test-ikisocket.yml rename to .github/workflows/test-socketio.yml index 7b05f5ff..027a6d6a 100644 --- a/.github/workflows/test-ikisocket.yml +++ b/.github/workflows/test-socketio.yml @@ -1,4 +1,4 @@ -name: "Test ikisocket" +name: "Test Socket.io" on: push: @@ -6,10 +6,10 @@ on: - master - main paths: - - "ikisocket/**" + - "socketio/**" pull_request: paths: - - "ikisocket/**" + - "socketio/**" jobs: Tests: @@ -28,5 +28,5 @@ jobs: with: go-version: "${{ matrix.go-version }}" - name: Run Test - working-directory: ./ikisocket + working-directory: ./socketio run: go test -v -race ./... diff --git a/README.md b/README.md index a60084b6..c642e32c 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Repository for third party middlewares with dependencies. * [Fibersentry](./fibersentry/README.md) * [Fiberzap](./fiberzap/README.md) * [Fiberzerolog](./fiberzerolog/README.md) -* [Ikisocket](./ikisocket/README.md) +* [Socket.io](./socketio/README.md) * [JWT](./jwt/README.md) * [Loadshed](./loadshed/README.md) * [NewRelic](./fibernewrelic/README.md) diff --git a/ikisocket/README.md b/socketio/README.md similarity index 90% rename from ikisocket/README.md rename to socketio/README.md index 78e54595..49c8e9a8 100644 --- a/ikisocket/README.md +++ b/socketio/README.md @@ -1,9 +1,9 @@ -id: ikisocket +id: socketio --- -# Ikisocket +# Socket.io -![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=ikisocket*) +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=socketio*) ![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) ![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) ![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) @@ -16,13 +16,13 @@ WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events supp ``` go get -u github.com/gofiber/fiber/v2 -go get -u github.com/gofiber/contrib/ikisocket +go get -u github.com/gofiber/contrib/socketio ``` ## Signatures ```go -// Initialize new ikisocket in the callback this will +// Initialize new socketio in the callback this will // execute a callback that expects kws *Websocket Object func New(callback func(kws *Websocket)) func(*fiber.Ctx) error ``` @@ -64,7 +64,7 @@ import ( "fmt" "log" - "github.com/gofiber/contrib/ikisocket" + "github.com/gofiber/contrib/socketio" "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" ) @@ -97,12 +97,12 @@ func main() { }) // Multiple event handling supported - ikisocket.On(ikisocket.EventConnect, func(ep *ikisocket.EventPayload) { + socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) { fmt.Println(fmt.Sprintf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))) }) // Custom event handling supported - ikisocket.On("CUSTOM_EVENT", func(ep *ikisocket.EventPayload) { + socketio.On("CUSTOM_EVENT", func(ep *socketio.EventPayload) { fmt.Println(fmt.Sprintf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))) // ---> @@ -112,7 +112,7 @@ func main() { }) // On message event - ikisocket.On(ikisocket.EventMessage, func(ep *ikisocket.EventPayload) { + socketio.On(socketio.EventMessage, func(ep *socketio.EventPayload) { fmt.Println(fmt.Sprintf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))) @@ -138,14 +138,14 @@ func main() { } // Emit the message directly to specified user - err = ep.Kws.EmitTo(clients[message.To], ep.Data, ikisocket.TextMessage) + err = ep.Kws.EmitTo(clients[message.To], ep.Data, socketio.TextMessage) if err != nil { fmt.Println(err) } }) // On disconnect event - ikisocket.On(ikisocket.EventDisconnect, func(ep *ikisocket.EventPayload) { + socketio.On(socketio.EventDisconnect, func(ep *socketio.EventPayload) { // Remove the user from the local clients delete(clients, ep.Kws.GetStringAttribute("user_id")) fmt.Println(fmt.Sprintf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))) @@ -153,34 +153,34 @@ func main() { // On close event // This event is called when the server disconnects the user actively with .Close() method - ikisocket.On(ikisocket.EventClose, func(ep *ikisocket.EventPayload) { + socketio.On(socketio.EventClose, func(ep *socketio.EventPayload) { // Remove the user from the local clients delete(clients, ep.Kws.GetStringAttribute("user_id")) fmt.Println(fmt.Sprintf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))) }) // On error event - ikisocket.On(ikisocket.EventError, func(ep *ikisocket.EventPayload) { + socketio.On(socketio.EventError, func(ep *socketio.EventPayload) { fmt.Println(fmt.Sprintf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))) }) - app.Get("/ws/:id", ikisocket.New(func(kws *ikisocket.Websocket) { + app.Get("/ws/:id", socketio.New(func(kws *socketio.Websocket) { // Retrieve the user id from endpoint userId := kws.Params("id") // Add the connection to the list of the connected clients // The UUID is generated randomly and is the key that allow - // ikisocket to manage Emit/EmitTo/Broadcast + // socketio to manage Emit/EmitTo/Broadcast clients[userId] = kws.UUID // Every websocket connection has an optional session key => value storage kws.SetAttribute("user_id", userId) //Broadcast to all the connected users the newcomer - kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, ikisocket.TextMessage) + kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, socketio.TextMessage) //Write welcome message - kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), ikisocket.TextMessage) + kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), socketio.TextMessage) })) log.Fatal(app.Listen(":3000")) diff --git a/ikisocket/go.mod b/socketio/go.mod similarity index 95% rename from ikisocket/go.mod rename to socketio/go.mod index b6673a4a..79c0431f 100644 --- a/ikisocket/go.mod +++ b/socketio/go.mod @@ -1,4 +1,4 @@ -module github.com/gofiber/contrib/ikisocket +module github.com/gofiber/contrib/socketio go 1.20 diff --git a/ikisocket/go.sum b/socketio/go.sum similarity index 100% rename from ikisocket/go.sum rename to socketio/go.sum diff --git a/ikisocket/ikisocket.go b/socketio/socketio.go similarity index 99% rename from ikisocket/ikisocket.go rename to socketio/socketio.go index 87032493..599e5438 100644 --- a/ikisocket/ikisocket.go +++ b/socketio/socketio.go @@ -1,4 +1,4 @@ -package ikisocket +package socketio import ( "context" diff --git a/ikisocket/ikisocket_test.go b/socketio/socketio_test.go similarity index 99% rename from ikisocket/ikisocket_test.go rename to socketio/socketio_test.go index f33cae83..c22d7e7c 100644 --- a/ikisocket/ikisocket_test.go +++ b/socketio/socketio_test.go @@ -1,4 +1,4 @@ -package ikisocket +package socketio import ( "context" From 9b36367a15349f9c6b08d30256019bbedec64f1f Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sat, 17 Feb 2024 12:11:29 -0500 Subject: [PATCH 17/18] Update README.md --- socketio/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socketio/README.md b/socketio/README.md index 49c8e9a8..f68c78c4 100644 --- a/socketio/README.md +++ b/socketio/README.md @@ -10,7 +10,7 @@ id: socketio WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired by [Socket.io](https://github.com/socketio/socket.io) -**Note: Requires Go 1.18 and above** +**Note: Requires Go 1.20 and above** ## Install From e124f5608e36ea06edbe324b5156a33f4e0e4649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 18 Feb 2024 20:01:44 +0100 Subject: [PATCH 18/18] fix naming and sorting --- .github/workflows/release-drafter-socketio.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-drafter-socketio.yml b/.github/workflows/release-drafter-socketio.yml index 7005170e..a140ccba 100644 --- a/.github/workflows/release-drafter-socketio.yml +++ b/.github/workflows/release-drafter-socketio.yml @@ -8,7 +8,7 @@ on: paths: - 'socketio/**' jobs: - draft_release_websocket: + draft_release_socketio: runs-on: ubuntu-latest timeout-minutes: 30 steps: diff --git a/README.md b/README.md index c642e32c..da02e963 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,12 @@ Repository for third party middlewares with dependencies. * [Fibersentry](./fibersentry/README.md) * [Fiberzap](./fiberzap/README.md) * [Fiberzerolog](./fiberzerolog/README.md) -* [Socket.io](./socketio/README.md) * [JWT](./jwt/README.md) * [Loadshed](./loadshed/README.md) * [NewRelic](./fibernewrelic/README.md) * [Open Policy Agent](./opafiber/README.md) * [Otelfiber (OpenTelemetry)](./otelfiber/README.md) * [Paseto](./paseto/README.md) +* [Socket.io](./socketio/README.md) * [Swagger](./swagger/README.md) * [Websocket](./websocket/README.md)