event

package
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 9 Imported by: 0

README


id: websocket-event

WebSocket Event

Plain WebSocket event helper for Fiber, built on top of github.com/gofiber/contrib/v3/websocket.

This package is for applications that want the legacy event-bus behavior over ordinary WebSocket clients. It does not implement the Engine.IO or Socket.IO protocol. Use github.com/gofiber/contrib/v3/socketio when you need compatibility with the official socket.io-client package.

If your application used the older socketio package as a plain WebSocket event bus, migrate the import to github.com/gofiber/contrib/v3/websocket/event. The API intentionally stays close to that legacy event helper, while the socketio package is reserved for the Socket.IO protocol.

Compatible with Fiber v3.

Install

go get -u github.com/gofiber/contrib/v3/websocket

The event helper is the event subpackage of that module:

import "github.com/gofiber/contrib/v3/websocket/event"

Signatures

Create a handler:

func New(callback func(kws *event.Websocket), config ...websocket.Config) fiber.Handler
func NewWithConfig(callback func(kws *event.Websocket), eventCfg event.Config, wsConfig ...websocket.Config) fiber.Handler

Register listeners. Package-level On is process-global; the (*Websocket) method form is scoped to a single connection (see Listeners):

type EventCallback func(payload *event.EventPayload)

func On(name string, callback EventCallback)   // global: fires for every connection
func Off(name string)                          // removes global listeners for name

func (kws *Websocket) On(name string, callback EventCallback) // this connection only
func (kws *Websocket) Off(name string)                        // remove this connection's listeners

Send messages. The (*Websocket) method forms operate on / from a specific connection and fire EventError on failure; the package-level forms address connections in the global pool and do not fire EventError (see Sending messages):

// Method forms (fire EventError on failure).
func (kws *Websocket) Emit(message []byte, mType ...int)
func (kws *Websocket) EmitTo(uuid string, message []byte, mType ...int) error
func (kws *Websocket) EmitToList(uuids []string, message []byte, mType ...int)
func (kws *Websocket) Broadcast(message []byte, except bool, mType ...int)
func (kws *Websocket) Fire(name string, data []byte)

// Package forms (do not fire EventError).
func EmitTo(uuid string, message []byte, mType ...int) error
func EmitToList(uuids []string, message []byte, mType ...int)
func Broadcast(message []byte, mType ...int)
func Fire(name string, data []byte)

Connection identity and attributes:

func (kws *Websocket) GetUUID() string
func (kws *Websocket) SetUUID(uuid string) error // returns ErrorUUIDDuplication on conflict
func (kws *Websocket) SetAttribute(key string, value interface{})
func (kws *Websocket) GetAttribute(key string) interface{}

Graceful shutdown:

func Drain()
func IsDraining() bool
func CloseAll(ctx context.Context, code int, reason string) error

Listeners

On registers a process-global listener: the callback fires for the given event on every connection created by New / NewWithConfig, regardless of route or Config. Listeners are additive and stay registered until removed with Off. This matches the legacy socketio event bus.

event.On(event.EventMessage, func(ep *event.EventPayload) { /* ... */ })
event.Off(event.EventMessage) // remove again, e.g. on reconfiguration or in tests

For listeners that should fire for a single connection only, use the (*Websocket).On method (typically from the New callback). Per-connection listeners fire in addition to the global ones and are discarded automatically when the connection disconnects:

app.Get("/ws/:id", event.New(func(kws *event.Websocket) {
    kws.On(event.EventMessage, func(ep *event.EventPayload) {
        // only fires for this connection
    })
}))

Sending messages

There are two flavors of EmitTo / EmitToList / Broadcast, and the difference is easy to miss:

Form Targets On failure
(*Websocket).EmitTo a UUID in the pool fires EventError on kws and returns the error
(*Websocket).EmitToList a list of UUIDs fires EventError on kws per failed UUID
(*Websocket).Broadcast all connections (except skips kws itself) fires EventError on kws per failed UUID
EmitTo (package) a UUID in the pool returns the error, does not fire EventError
EmitToList (package) a list of UUIDs silently ignores per-UUID errors
Broadcast (package) all connections fire-and-forget, no error feedback

Use the method forms when you want delivery failures surfaced as EventError events; use the package forms for fire-and-forget fan-out. Emit enqueues the message on the connection's outbound queue, which a dedicated goroutine drains in order; if the queue is full it blocks until a slot frees up or the connection closes.

Configuration

Per-instance tuning via event.Config passed to NewWithConfig. Zero values fall back to the matching package-level var, which itself falls back to the hard default.

Config field Default Description
PingInterval 1s Interval between server-originated Ping frames. Must be less than any upstream proxy or load balancer idle timeout.
ReadIdleTimeout 3 * PingInterval Maximum silence before the read deadline fires and the connection is disconnected.
WriteTimeout 10s Bounds a single WriteMessage / WriteControl call.
MaxMessageSize 1 MiB Inbound frame size limit. Set to math.MaxInt64 to opt out.
SendQueueSize 100 Per-connection outbound message queue capacity.
MaxSendRetry 5 Max retries for transient socket write readiness issues.
RetrySendTimeout 20ms Backoff between retries while the connection is not ready.
RecoverHandler nil Called on a panic inside a user On callback. If nil, panics are recovered silently.

The legacy package-level vars (PongTimeout, RetrySendTimeout, MaxSendRetry, SendQueueSize, ReadTimeout) are still read once per connection at upgrade time for backwards compatibility, but mutating them after a connection is established has no effect on running goroutines. Prefer NewWithConfig for new code.

Thread safety

On, Off, Fire, the package-level EmitTo / EmitToList / Broadcast, and the connection pool are safe for concurrent use. A single *Websocket is safe to use from multiple goroutines: reads and writes are serialized through a per-connection mutex and an outbound send queue. Listener callbacks run on the helper's goroutines, so a callback that blocks holds up event delivery for that connection; offload long work to your own goroutine.

Graceful Shutdown

The helper keeps an in-process pool of active connections. Use event.Drain and event.CloseAll together with a Fiber shutdown hook so clients receive a clean 1001 Going Away close frame instead of an abrupt TCP reset:

app.Hooks().OnShutdown(func() error {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    event.Drain()
    return event.CloseAll(ctx, websocket.CloseGoingAway, "server shutting down")
})

If ctx expires before every goroutine exits, CloseAll force-closes the remaining underlying connections and returns ctx.Err().

Drain only flips the draining flag; it does not refuse new connections by itself. Gate the upgrade route on IsDraining to stop accepting clients during shutdown:

app.Use("/ws", func(c fiber.Ctx) error {
    if event.IsDraining() {
        return fiber.NewError(fiber.StatusServiceUnavailable, "shutting down")
    }
    if websocket.IsWebSocketUpgrade(c) {
        return c.Next()
    }
    return fiber.ErrUpgradeRequired
})

Example

package main

import (
	"log"

	"github.com/gofiber/contrib/v3/websocket"
	"github.com/gofiber/contrib/v3/websocket/event"
	"github.com/gofiber/fiber/v3"
)

func main() {
	app := fiber.New()

	app.Use("/ws", func(c fiber.Ctx) error {
		if websocket.IsWebSocketUpgrade(c) {
			return c.Next()
		}
		return fiber.ErrUpgradeRequired
	})

	event.On(event.EventMessage, func(ep *event.EventPayload) {
		ep.Kws.Emit([]byte("echo: "+string(ep.Data)), event.TextMessage)
	})

	app.Get("/ws/:id", event.New(func(kws *event.Websocket) {
		// kws.Params / kws.Locals / kws.Query / kws.Cookies wrap the Fiber
		// request context captured at upgrade time.
		kws.SetAttribute("user_id", kws.Params("id"))
	}))

	log.Fatal(app.Listen(":3000"))
}

Custom events

Event names are arbitrary strings. Register a listener with On and trigger it with Fire (on one connection) or the package-level Fire (on all):

event.On("notify", func(ep *event.EventPayload) {
    log.Printf("notify %s: %s", ep.SocketUUID, ep.Data)
})

// from a connection:
kws.Fire("notify", []byte("hello"))

// to every active connection:
event.Fire("notify", []byte("broadcast"))

Supported Events

Const Event Description
EventMessage message Fired when a text or binary message is received.
EventPing ping Fired when a WebSocket ping control frame is received.
EventPong pong Fired when a WebSocket pong control frame is received.
EventDisconnect disconnect Fired when the connection is closed. On an error close, EventError fires too.
EventConnect connect Fired after the New callback runs, before the read loop starts.
EventClose close Fired when the server actively closes the connection.
EventError error Fired on a failed EmitTo, a dropped outbound message, or an error-driven disconnect.

Event Payload

Field Type Description
Kws *event.Websocket The connection object.
Name string The event name.
SocketUUID string Unique connection UUID.
SocketAttributes map[string]any Snapshot of optional connection attributes.
Error error Optional error for disconnect and error events.
Data []byte Data used on message, custom, and error events.

Documentation

Overview

Package event provides a plain WebSocket event helper built on top of the websocket middleware.

Index

Constants

View Source
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 frame.
	PingMessage = 9
	// PongMessage denotes a pong control frame.
	PongMessage = 10
)

Source @url:https://github.com/gorilla/websocket/blob/master/conn.go#L61 The message types are defined in RFC 6455, section 11.8.

View Source
const (
	// EventMessage is fired when a text or binary message is received.
	EventMessage = "message"
	// EventPing is fired when a WebSocket ping control frame is received.
	EventPing = "ping"
	// EventPong is fired when a WebSocket pong control frame is received.
	EventPong = "pong"
	// EventDisconnect is fired when the connection is closed. When the close is
	// caused by an error, EventError is fired as well (with the same error).
	EventDisconnect = "disconnect"
	// EventConnect is fired when the connection is initialized, immediately
	// after the New / NewWithConfig callback has run and before the read loop
	// starts.
	EventConnect = "connect"
	// EventClose is fired when the server actively closes the connection (for
	// example via Close or CloseAll).
	EventClose = "close"
	// EventError is fired when an error occurs, including a failed EmitTo, a
	// dropped outbound message, and error-driven disconnects.
	EventError = "error"
)

Supported event list.

Variables

View Source
var (
	// ErrorInvalidConnection indicates that the addressed connection is no
	// longer available.
	ErrorInvalidConnection = errors.New("message cannot be delivered invalid/gone connection")
	// ErrorUUIDDuplication indicates that the UUID already exists in the pool.
	ErrorUUIDDuplication = errors.New("UUID already exists in the available connections pool")
)
View Source
var (
	// PongTimeout is the interval between server-originated Ping frames.
	// Despite its name, this helper uses Ping for liveness; the historical
	// name is preserved for backwards compatibility. The value must be less
	// than any upstream proxy or load balancer idle timeout.
	//
	// Deprecated: prefer Config.PingInterval passed to NewWithConfig. The
	// package-level value is read once per connection at upgrade time;
	// mutating it after that has no effect on running connections.
	PongTimeout = time.Second
	// RetrySendTimeout controls how long a queued message waits before retrying.
	RetrySendTimeout = 20 * time.Millisecond
	// MaxSendRetry defines the max retries for transient socket write issues.
	MaxSendRetry = 5
	// SendQueueSize controls the per-connection outbound message queue size.
	SendQueueSize = 100
	// ReadTimeout is deprecated and no longer used; reads block until a
	// message arrives or the connection is closed.
	//
	// Deprecated: ReadTimeout is a no-op. Configure Config.ReadIdleTimeout
	// on NewWithConfig for the actual read deadline behaviour.
	ReadTimeout = 10 * time.Millisecond
)

Functions

func Broadcast

func Broadcast(message []byte, mType ...int)

Broadcast emits to all active connections.

func CloseAll

func CloseAll(ctx context.Context, code int, reason string) error

CloseAll iterates every active connection in the in-process pool and sends a close control frame with the supplied code and reason, then waits for each helper's run() loop to exit. If ctx expires first, remaining connections are force closed via Conn.Close.

The typical usage is from a Fiber shutdown hook:

app.Hooks().OnShutdown(func() error {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    event.Drain()
    return event.CloseAll(ctx, websocket.CloseGoingAway, "server shutting down")
})

Reason is capped at 123 bytes per RFC 6455.

func Drain

func Drain()

Drain marks the package as draining. New connections are not refused automatically; the upgrade gate is the caller's responsibility (a middleware that checks IsDraining and returns 503).

func EmitTo

func EmitTo(uuid string, message []byte, mType ...int) error

EmitTo emits a message to a connection UUID. It returns the error to the caller and, unlike the (*Websocket).EmitTo method form, does not fire EventError.

func EmitToList

func EmitToList(uuids []string, message []byte, mType ...int)

EmitToList emits a message to a list of connection UUIDs. Per-UUID errors are silently ignored and, unlike the (*Websocket).EmitToList method form, no EventError is fired.

func Fire

func Fire(event string, data []byte)

Fire fires a custom event on all active connections.

func IsDraining

func IsDraining() bool

IsDraining reports whether the package is in draining mode. Upgrade handlers can poll this to refuse new connections during a graceful shutdown.

func New

func New(callback func(kws *Websocket), config ...websocket.Config) fiber.Handler

New returns a Fiber handler that upgrades the request to WebSocket and wraps it with the event helper using default tuning. For per-instance tuning use NewWithConfig.

func NewWithConfig

func NewWithConfig(callback func(kws *Websocket), eventCfg Config, wsConfig ...websocket.Config) fiber.Handler

NewWithConfig returns a Fiber handler that upgrades the request to WebSocket and wraps it with the event helper, using the supplied per-instance tuning.

func Off

func Off(event string)

Off removes all process-global listeners registered for the event via On. It is the counterpart to On and does not affect per-connection listeners registered through (*Websocket).On.

func On

func On(event string, callback EventCallback)

On registers a process-global listener for an event. The callback fires for that event on every connection created by New / NewWithConfig, regardless of route or Config, and stays registered until removed with Off. For listeners scoped to a single connection use the (*Websocket).On method instead.

Types

type Config

type Config struct {
	// PingInterval is the interval between server-originated Ping frames.
	// Must be less than any upstream proxy or load balancer idle timeout.
	// Zero falls back to PongTimeout, then 1s.
	PingInterval time.Duration
	// ReadIdleTimeout bounds how long a connection may stay silent before
	// it is considered dead. Zero falls back to 3 * PingInterval.
	ReadIdleTimeout time.Duration
	// WriteTimeout bounds a single WriteMessage or WriteControl call. Zero
	// falls back to 10s.
	WriteTimeout time.Duration
	// MaxMessageSize bounds the largest inbound frame in bytes. Zero falls
	// back to 1 MiB. To opt out, set math.MaxInt64.
	MaxMessageSize int64
	// SendQueueSize is the per-connection outbound buffer. Zero falls back
	// to SendQueueSize package var, then 100.
	SendQueueSize int
	// MaxSendRetry caps retries for transient socket write issues. Zero
	// falls back to MaxSendRetry package var, then 5.
	MaxSendRetry int
	// RetrySendTimeout is the wait between retries. Zero falls back to
	// RetrySendTimeout package var, then 20ms.
	RetrySendTimeout time.Duration
	// RecoverHandler is called on a panic inside a user On callback. If
	// nil, panics are recovered silently.
	RecoverHandler func(event string, r any)
}

Config tunes a single event helper instance. Zero values fall back to the matching deprecated package-level var, which itself falls back to the historical default. Pass via NewWithConfig.

type EventCallback

type EventCallback func(payload *EventPayload)

EventCallback is the listener signature invoked when an event fires.

type EventPayload

type EventPayload struct {
	// Kws is the connection object.
	Kws *Websocket
	// Name is the event name.
	Name string
	// SocketUUID is the unique connection UUID.
	SocketUUID string
	// SocketAttributes is a snapshot of connection attributes.
	SocketAttributes map[string]any
	// Error is populated for disconnect and error events.
	Error error
	// Data is used on message, custom, and error events.
	Data []byte
}

EventPayload stores information about an event and its connection.

type Websocket

type Websocket struct {

	// Conn is the underlying Fiber websocket connection.
	Conn *websocket.Conn

	// UUID is the unique connection identifier.
	UUID string
	// Locals wraps Fiber Locals.
	Locals func(key string) interface{}
	// Params wraps Fiber Params.
	Params func(key string, defaultValue ...string) string
	// Query wraps Fiber Query.
	Query func(key string, defaultValue ...string) string
	// Cookies wraps Fiber Cookies.
	Cookies func(key string, defaultValue ...string) string
	// contains filtered or unexported fields
}

Websocket wraps a websocket.Conn with event-bus helpers.

func (*Websocket) Broadcast

func (kws *Websocket) Broadcast(message []byte, except bool, mType ...int)

Broadcast emits to all active connections, skipping the originating connection (kws) when except is true. Each failed target fires EventError on kws; the package-level Broadcast does not.

func (*Websocket) Close

func (kws *Websocket) Close()

Close actively closes the current connection from the server.

func (*Websocket) Emit

func (kws *Websocket) Emit(message []byte, mType ...int)

Emit writes a message to the current connection.

func (*Websocket) EmitTo

func (kws *Websocket) EmitTo(uuid string, message []byte, mType ...int) error

EmitTo emits a message to a connection UUID. On an invalid or dead target it fires EventError on the originating connection (kws) and returns the error; the package-level EmitTo does not fire EventError.

func (*Websocket) EmitToList

func (kws *Websocket) EmitToList(uuids []string, message []byte, mType ...int)

EmitToList emits a message to a list of connection UUIDs. Each failed UUID fires EventError on the originating connection (kws); unlike the package-level EmitToList, errors are not silently ignored.

func (*Websocket) Fire

func (kws *Websocket) Fire(event string, data []byte)

Fire fires a custom event on the current connection.

func (*Websocket) GetAttribute

func (kws *Websocket) GetAttribute(key string) interface{}

GetAttribute returns an attribute from the connection.

func (*Websocket) GetIntAttribute

func (kws *Websocket) GetIntAttribute(key string) int

GetIntAttribute retrieves an attribute as an int.

func (*Websocket) GetStringAttribute

func (kws *Websocket) GetStringAttribute(key string) string

GetStringAttribute retrieves an attribute as a string.

func (*Websocket) GetUUID

func (kws *Websocket) GetUUID() string

GetUUID returns the connection UUID.

func (*Websocket) IsAlive

func (kws *Websocket) IsAlive() bool

IsAlive reports whether the connection is active.

func (*Websocket) Off

func (kws *Websocket) Off(event string)

Off removes all listeners registered for the event on this connection via the On method. It does not affect process-global listeners registered with the package-level On.

func (*Websocket) On

func (kws *Websocket) On(event string, callback EventCallback)

On registers a listener for the event on this connection only. Unlike the package-level On, which is process-global, these listeners fire only for events on this connection and are discarded when it disconnects. Both per-connection and global listeners fire for a given event.

func (*Websocket) SetAttribute

func (kws *Websocket) SetAttribute(key string, attribute interface{})

SetAttribute sets an attribute for the connection.

func (*Websocket) SetUUID

func (kws *Websocket) SetUUID(uuid string) error

SetUUID updates the connection UUID and its pool entry.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL