Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer socketio middleware to contrib #956

Merged
merged 20 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
50 changes: 50 additions & 0 deletions .github/release-drafter-ikisocket.yml
Original file line number Diff line number Diff line change
@@ -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...ikisocket/v$RESOLVED_VERSION

Thank you $CONTRIBUTORS for making this update possible.
19 changes: 19 additions & 0 deletions .github/workflows/release-drafter-ikisocket.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Release Drafter ikisocket
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 }}
33 changes: 33 additions & 0 deletions .github/workflows/test-ikisocket.yml
Original file line number Diff line number Diff line change
@@ -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
antoniodipinto marked this conversation as resolved.
Show resolved Hide resolved
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 ./...
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Repository for third party middlewares with dependencies.
* [Fibersentry](./fibersentry/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+fibersentry%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-fibersentry.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [Fiberzap](./fiberzap/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+fiberzap%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-fiberzap.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [Fiberzerolog](./fiberzerolog/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+fiberzerolog%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-fiberzerolog.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [Ikisocket](./ikisocket/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+ikisocket%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-ikisocket.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [JWT](./jwt/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+jwt%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-jwt.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [Loadshed](./loadshed/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+Loadshed%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-loadshed.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
* [NewRelic](./fibernewrelic/README.md) <a href="https://github.com/gofiber/contrib/actions?query=workflow%3A%22Test+fibernewrelic%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/contrib/test-fibernewrelic.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
Expand Down
235 changes: 235 additions & 0 deletions ikisocket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
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": "<user-id>",
// "to": "<recipient-user-id>",
// "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

| Const | Event | Description |
|:----------------|:-------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| EventMessage | `message` | Fired when a Text/Binary message is received |
| EventPing | `ping` | 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> |
| 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 |

## 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 |

**Note: the FastHTTP connection can be accessed directly from the instance**

```go
kws.Conn
```
13 changes: 13 additions & 0 deletions ikisocket/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module github.com/antoniodipinto/ikisocket

go 1.18
antoniodipinto marked this conversation as resolved.
Show resolved Hide resolved

require (
github.com/fasthttp/websocket v1.5.4
github.com/gofiber/contrib/websocket v1.2.0
github.com/gofiber/fiber/v2 v2.50.0
antoniodipinto marked this conversation as resolved.
Show resolved Hide resolved
github.com/google/uuid v1.3.1
antoniodipinto marked this conversation as resolved.
Show resolved Hide resolved
github.com/rivo/uniseg v0.4.4 // indirect
github.com/stretchr/testify v1.8.4
github.com/valyala/fasthttp v1.50.0
)
Loading
Loading