socketio

package module
v1.2.1 Latest Latest
Warning

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

Go to latest
Published: May 13, 2026 License: MIT Imports: 15 Imported by: 0

README


id: socketio

Socket.io

Release Discord Test

WebSocket wrapper for Fiber that implements the Engine.IO v4 / Socket.IO v5 wire protocol, making it fully compatible with the official socket.io-client library.

Compatible with Fiber v3.

Features

This middleware implements the full Engine.IO v4 / Socket.IO v5 wire protocol. Highlights:

  • Synchronous handshake. The Engine.IO OPEN / Socket.IO CONNECT exchange completes before the user New() callback returns, so emits issued inside the callback are ordered after the handshake reply.
  • HTTP long-polling fallback (opt-in). Set socketio.EnablePolling = true and mount the same handler for GET and POST to accept transport=polling clients. Polling sessions speak the same Engine.IO v4 / Socket.IO v5 wire protocol over HTTP and route through the same listener API (Emit, Ack, Close, Broadcast). Polling-to-WebSocket transport upgrade is not yet implemented; sessions that connect via polling stay on polling.
  • Namespaces and handshake auth. The negotiated namespace is honoured for inbound and outbound packets; the client's connect-time auth payload is exposed via Websocket.HandshakeAuth() and EventPayload.HandshakeAuth.
  • Inbound acks. Client-initiated callbacks surface as EventPayload.HasAck / AckID; reply once with payload.Ack(args...).
  • Outbound acks. Server-initiated EmitWithAck, EmitWithAckTimeout, and EmitWithAckArgs round-trip a callback id and invoke the supplied callback when the client acks (or on timeout/disconnect).
  • Multi-arg events. Inbound events expose every argument tuple as EventPayload.Args [][]byte; outbound EmitArgs / EmitWithAckArgs send pre-encoded JSON tuples.
  • Deterministic heartbeat. Server PINGs every PingInterval; the connection is torn down if no PONG arrives within PingTimeout.
  • EIO 0x1E batched frames. Multi-packet WebSocket frames separated by ASCII RS (0x1E) are parsed correctly, with a hard cap (MaxBatchPackets) to prevent slice-header amplification.
  • Reserved-event-name guard. User code cannot register or emit names reserved by the protocol (e.g. connect, disconnect).
  • EIO version validation. Handshakes that advertise an unsupported EIO version are rejected.
  • Auth payload validation. The auth blob must be a JSON object and is bounded by MaxAuthPayload; oversize or malformed payloads are answered with CONNECT_ERROR.
  • DoS hardening. MaxPayload, MaxBatchPackets, MaxEventNameLength, and MaxAuthPayload bound every attacker-controlled length.
  • Lock-free listener registry plus atomic.Bool isAlive, removing the per-event mutex from the hot path.
  • Optional drop-frames-on-overflow. When DropFramesOnOverflow is true, a saturated send queue drops the offending frame and fires EventError instead of tearing down the connection.
  • Graceful drain. The package-level Shutdown(ctx) closes every active socket and waits for each worker to exit (or until ctx is cancelled).

Known limitations

  • One namespace per Engine.IO connection. Each WebSocket binds the namespace negotiated during the SIO CONNECT packet; multiplexing several namespaces over one EIO connection is not supported.
  • No BINARY_EVENT (5) / BINARY_ACK (6). Binary Socket.IO frames are passed through as raw EventMessage data; attachment reassembly is not implemented.
  • No connection-state recovery. Resume-on-reconnect (Socket.IO's connectionStateRecovery feature) is not implemented; reconnects always start a fresh session.
  • No polling-to-WebSocket transport upgrade. When polling is enabled, sessions that open with transport=polling advertise an empty upgrades array and stay on polling for the session lifetime. Clients that need WebSocket from the start should configure transports: ['websocket'].
  • No JSONP polling fallback. JSONP requests (?j=N) are rejected with engine.io error code 3. Modern browsers use XHR2/fetch; JSONP support is not planned.
  • CORS is not handled by the middleware. Mount github.com/gofiber/fiber/v3/middleware/cors (or your preferred CORS middleware) upstream of the polling route to control the policy. Long-poll holds connections open for up to ~25s by default, so reverse-proxy timeouts must accommodate (e.g. nginx proxy_read_timeout >= 60s and proxy_buffering off).
Production hardening notes
  • Rate limiting. Each polling open allocates a *Websocket plus 2 short-lived goroutines. With EnablePolling = true an unauthenticated client can create sessions until HandshakeTimeout reaps idle ones (10s default). Mount github.com/gofiber/fiber/v3/middleware/limiter upstream of the route to bound concurrent session creation.
  • Write timeout. A long-poll GET response that the client never reads pins a fasthttp worker on TCP backpressure. Configure fiber.Config{WriteTimeout: ...} (a few seconds is typically appropriate) so abandoned reads do not strand workers.
  • Burst sizing. PollQueueMaxFrames (default 1024) bounds the per-session outbound buffer. With the default DropFramesOnOverflow = false a synchronous burst of more than 1024 emits inside a single listener call disconnects the session with ErrSendQueueClosed. Either pace large bursts across drains, raise PollQueueMaxFrames, or set DropFramesOnOverflow = true to tolerate overflow at the cost of dropped frames + EventError.
  • Listener panics. Both transports recover panics inside the New() callback and inside event listeners; the panic value is logged via the package Logger hook. Avoid panic(string(attackerControlledBytes)) to prevent log injection in downstream consumers.

Configuration

All tunables are package-level variables; override before the first connection is accepted.

Variable Default Meaning
PingInterval 25s How often the server emits Engine.IO PING.
PingTimeout 20s Grace window for the client PONG before the connection is killed.
HandshakeTimeout 10s Hard deadline for completing EIO OPEN + SIO CONNECT.
MaxPayload 1_000_000 (1 MB) Max bytes per inbound WebSocket frame; advertised to the client.
MaxAuthPayload 8 KiB Max bytes for the SIO CONNECT auth JSON.
MaxBatchPackets 256 Max EIO packets in a single 0x1E-batched frame.
MaxEventNameLength 256 Max length of an inbound SIO event name.
OutboundAckTimeout 30s Default ack deadline for EmitWithAck.
SendQueueSize 100 Capacity of the per-connection outbound queue.
DropFramesOnOverflow false If true, drop the offending frame on overflow (fires EventError).
RetrySendTimeout 20ms Back-off between send retries.
MaxSendRetry 5 Max send retries before a frame is dropped.
ReadTimeout 10ms Deprecated: no longer consulted by the read loop; kept for backward compatibility.
EnablePolling false If true, the handler returned from New also serves Engine.IO HTTP long-polling on GET/POST.
PollingMaxBufferSize 1_000_000 Cap on a single polling HTTP body (request POST or response GET drain).
MaxPollWait 30s Maximum time a long-poll GET blocks waiting for outbound frames.
PollQueueMaxFrames 1024 Cap on buffered outbound frames per polling session; overflow honors DropFramesOnOverflow.

Use socketio.Shutdown(ctx) from fiber.App.ShutdownWithContext for a deterministic drain.

Go version support

We only support the latest two versions of Go. Visit https://go.dev/doc/devel/release for more information.

Install

go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/v3/socketio

Protocol compatibility

The middleware automatically handles the Engine.IO / Socket.IO handshake so you do not need any special server-side code; just point your socket.io-client at the WebSocket endpoint.

Required client configuration

The default socket.io-client transport order is ['polling', 'websocket']. The middleware supports both, but polling is opt-in:

WebSocket only (default):

import { io } from "socket.io-client";

const socket = io("http://localhost:3000", {
  path: "/ws",                 // match the Fiber route
  transports: ["websocket"],   // skip polling
});

Polling (or polling + websocket fallback): enable EnablePolling server-side and mount the handler for both GET and POST:

socketio.EnablePolling = true
h := socketio.New(func(kws *socketio.Websocket) { /* ... */ })
app.Get("/ws", h)
app.Post("/ws", h)
// Optionally allow CORS preflight:
// app.Options("/ws", h)
import { io } from "socket.io-client";

// Default transport order: polling first, then upgrade attempt. Since this
// implementation does not yet upgrade polling sessions to WebSocket, the
// session stays on polling. For a forced WebSocket connect use
// transports: ["websocket"]; for polling-only use transports: ["polling"].
const socket = io("http://localhost:3000", {
  path: "/ws",
});

CORS is not handled by the middleware. If your client connects from a different origin, mount your preferred CORS middleware (e.g. github.com/gofiber/fiber/v3/middleware/cors) upstream of the route. Long-polling holds a request open for up to ~25s by default, so reverse-proxy timeouts must accommodate (proxy_read_timeout >= 60s on nginx, proxy_buffering off).

Polling pitfalls
  • Forgot to mount POST. Polling clients send packets via POST; without app.Post(path, h) (or app.All(...)) the server returns 404 and the client loops with transport error. Always mount both GET and POST for polling routes.
  • kws.Conn is nil on polling. Use kws.IsPolling() to branch, or stick to the transport-agnostic Emit, EmitEvent, EmitArgs, EmitWithAck*, Broadcast, Ack, and Close methods. They all work identically on both transports.
  • Snapshot vs live request state. kws.Locals, kws.Params, kws.Query, kws.Cookies are captured at session-open time on polling sessions (because fasthttp recycles the request context after the OPEN handler returns). Store mutable per-connection data via kws.SetAttribute instead.
  • Burst bigger than PollQueueMaxFrames. With the default DropFramesOnOverflow = false, emitting more than PollQueueMaxFrames (1024) frames before any GET drains them tears the session down with ErrSendQueueClosed. Either pace bursts, raise PollQueueMaxFrames, or set DropFramesOnOverflow = true to drop the offending frames + fire EventError(ErrSendQueueOverflow) instead.
  • Body limit collision. If your Fiber app sets BodyLimit lower than PollingMaxBufferSize, fasthttp rejects the POST before our handler runs. Keep BodyLimit >= PollingMaxBufferSize.

Tunable globals

These package-level variables can be overridden before the first connection is accepted (typically in init() or early in main). They control timing and limits for the Engine.IO / Socket.IO transport.

Variable Default Description
PingInterval 25 * time.Second Interval between Engine.IO PING frames sent by the server to keep the connection alive.
PingTimeout 20 * time.Second How long the server waits for the client's PONG before considering the connection dead.
HandshakeTimeout 10 * time.Second Maximum time allowed for the Engine.IO / Socket.IO handshake (including namespace CONNECT) to complete.
MaxPayload 1 << 20 (1 MiB) Maximum size in bytes for a single inbound WebSocket frame; oversize messages close the socket.
MaxAuthPayload 8 << 10 (8 KiB) Maximum size in bytes for the Socket.IO CONNECT auth JSON.
MaxBatchPackets 256 Maximum number of Engine.IO packets accepted in a single 0x1E-batched frame.
MaxEventNameLength 256 Maximum length of an inbound Socket.IO event name.
OutboundAckTimeout 30 * time.Second Default timeout used by EmitWithAck when no per-call timeout is supplied.
DropFramesOnOverflow false If true, saturated outbound queues drop the offending frame and fire EventError.
RetrySendTimeout 20 * time.Millisecond Back-off between WebSocket send retries.
MaxSendRetry 5 Maximum number of WebSocket send retries before a frame is dropped.
EnablePolling false If true, the handler also accepts Engine.IO HTTP long-polling on GET/POST (opt-in fallback).
PollingMaxBufferSize 1_000_000 Cap on a single polling HTTP body (POST request body or GET drain response body), in bytes.
MaxPollWait 30 * time.Second Maximum time a long-poll GET blocks waiting for outbound frames before returning an empty 200.
PollQueueMaxFrames 1024 Maximum buffered outbound frames per polling session before overflow handling applies.
func init() {
    socketio.PingInterval = 15 * time.Second
    socketio.PingTimeout  = 10 * time.Second
    socketio.MaxPayload   = 4 << 20 // 4 MiB
}

Message format

All messages are exchanged as Socket.IO events.

Side API call Wire format
Server → Client kws.Emit([]byte("hello")) 42["message","hello"]
Server → Client kws.EmitEvent("greet", data) 42["greet",<data>]
Client → Server socket.emit("message", obj) fires EventMessage with obj
Client → Server socket.emit("custom", obj) fires the "custom" event

Note: Emit, EmitEvent, EmitArgs, and ack-emitting variants pass valid JSON through unchanged. Raw text bytes are encoded as JSON strings for compatibility with older examples.

Acks, namespaces, handshake auth

The middleware implements the full Socket.IO v5 ack flow and forwards the client's connect-time auth payload to your handlers.

Multi-argument emits

EmitArgs and EmitWithAckArgs accept a variadic list of values, so you can send richer event tuples without manually concatenating arrays. Valid JSON is passed through unchanged; raw text is encoded as a JSON string.

// 42["greet","hi",{"id":1}]
kws.EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`))
Server-initiated acks

EmitWithAck (and EmitWithAckTimeout) emit an event with an ack id and invoke the supplied callback once the client acks, or with an error when the timeout expires. EmitWithAck uses OutboundAckTimeout; EmitWithAckTimeout takes a per-call duration plus a structured AckCallback that distinguishes timeout from disconnect.

kws.EmitWithAckTimeout("ping", []byte(`"hello"`), 3*time.Second, func(ack []byte, err error) {
    if err != nil {
        log.Printf("ack failed: %v", err)
        return
    }
    // ack is the raw JSON the client passed to its callback (single value
    // or a JSON-array literal for multi-arg acks).
})
Client-initiated acks

When the client emits with a callback, the inbound event payload carries an ack id. Use HasAck and AckID to detect it, then send a single ack reply via EventPayload.Ack:

socketio.On("greet", func(ep *socketio.EventPayload) {
    if ep.HasAck {
        // ep.Args holds the raw JSON arguments the client sent.
        _ = ep.Ack([]byte(`"ok"`))
    }
})
Namespaces

The middleware honours the namespace negotiated during the Socket.IO CONNECT packet. Events emitted from the server are routed back on the same namespace the client joined; no extra configuration is required on the Go side.

Handshake auth

The client's auth payload must be a JSON object. It is parsed during the Socket.IO handshake and exposed to handlers as EventPayload.HandshakeAuth (raw JSON bytes). It is most commonly inspected on EventConnect:

// client
const socket = io("http://localhost:3000", {
  path: "/ws",
  transports: ["websocket"],
  auth: { token: "secret" },
});
socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) {
    // ep.HandshakeAuth == []byte(`{"token":"secret"}`)
    var auth struct{ Token string `json:"token"` }
    _ = json.Unmarshal(ep.HandshakeAuth, &auth)
})

Signatures

// Initialize new socketio in the callback this will
// execute a callback that expects kws *Websocket Object
// and optional config websocket.Config
func New(callback func(kws *Websocket), config ...websocket.Config) func(fiber.Ctx) error
// Add listener callback for an event into the listeners list
func On(event string, callback func(payload *EventPayload))
// Emit the message to a specific socket uuids list
// Ignores all errors
func EmitToList(uuids []string, message []byte, mType ...int)
// Emit to a specific socket connection
func EmitTo(uuid string, message []byte, mType ...int) error
// Broadcast to all the active connections
func Broadcast(message []byte, mType ...int)
// Fire custom event on all connections
func Fire(event string, data []byte)
// Emit a named event with multiple arguments
// (e.g. EmitArgs("greet", []byte(`"hi"`), []byte(`{"id":1}`)))
func (kws *Websocket) EmitArgs(event string, args ...[]byte)
// Emit a named event and invoke cb when the client acks (or on timeout /
// disconnect). The default deadline is OutboundAckTimeout. The callback
// receives the raw JSON ack value (or nil on timeout/disconnect).
func (kws *Websocket) EmitWithAck(event string, data []byte, cb func(ack []byte))
// Like EmitWithAck but with a per-call timeout and a structured AckCallback
// that distinguishes ErrAckTimeout from ErrAckDisconnected. Pass timeout = 0
// to disable the timeout.
func (kws *Websocket) EmitWithAckTimeout(event string, data []byte, timeout time.Duration, cb AckCallback)
// Multi-argument variant of EmitWithAck. The callback receives the slice of
// raw ack arguments the client supplied (or an error on timeout /
// disconnect). Uses OutboundAckTimeout.
func (kws *Websocket) EmitWithAckArgs(event string, args [][]byte, cb func([][]byte, error))
// HandshakeAuth returns the raw JSON auth payload sent by the client at
// connect time (nil if the client did not provide one).
func (kws *Websocket) HandshakeAuth() json.RawMessage
// Ack sends a Socket.IO ACK frame back to the client for the inbound event
// represented by this payload. Idempotent: only the first invocation
// produces a wire frame; later calls return ErrAckAlreadySent.
func (ep *EventPayload) Ack(args ...[]byte) error

Example

Go server

package main

import (
    "encoding/json"
    "fmt"
    "log"

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

// 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
    socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) {
        fmt.Printf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))
    })

    // Custom event handling supported
    socketio.On("CUSTOM_EVENT", func(ep *socketio.EventPayload) {
        fmt.Printf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))
        // --->

        // DO YOUR BUSINESS HERE

        // --->
    })

    // On message event
    socketio.On(socketio.EventMessage, func(ep *socketio.EventPayload) {

        fmt.Printf("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, socketio.TextMessage)
        if err != nil {
            fmt.Println(err)
        }
    })

    // On disconnect event
    socketio.On(socketio.EventDisconnect, func(ep *socketio.EventPayload) {
        // Remove the user from the local clients
        delete(clients, ep.Kws.GetStringAttribute("user_id"))
        fmt.Printf("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
    socketio.On(socketio.EventClose, func(ep *socketio.EventPayload) {
        // Remove the user from the local clients
        delete(clients, ep.Kws.GetStringAttribute("user_id"))
        fmt.Printf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))
    })

    // On error event
    socketio.On(socketio.EventError, func(ep *socketio.EventPayload) {
        fmt.Printf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))
    })

    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
        // 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
        newUserMsg, _ := json.Marshal(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID))
        kws.Broadcast(newUserMsg, true, socketio.TextMessage)

        // Write welcome message. Raw text is encoded as a JSON string.
        welcomeMsg, _ := json.Marshal(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID))
        kws.Emit(welcomeMsg, socketio.TextMessage)
    }))

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

TypeScript / JavaScript client

import { io } from "socket.io-client";

const socket = io("http://localhost:3000", {
  path: "/ws",
  transports: ["websocket"],
});

socket.on("connect", () => {
  console.log("connected, sid =", socket.id);

  // Send a message to the server
  socket.emit("message", {
    from: "user1",
    to:   "user2",
    event: "",
    data: "hello",
  });
});

socket.on("message", (data: unknown) => {
  console.log("received message:", data);
});

socket.on("disconnect", (reason) => {
  console.log("disconnected:", reason);
});

Supported events

Const Event Description
EventMessage message Fired when a socket.emit("message", …) event is received from the client
EventPing ping Fired when a WebSocket PING control frame is received (RFC 6455). Engine.IO PING is server-originated and not surfaced via this event.
EventPong pong Fired when an Engine.IO PONG ("3") replies to the server's heartbeat or when a WebSocket PONG control frame is received.
EventDisconnect disconnect Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7.
EventConnect connect Fired after the Engine.IO / Socket.IO handshake completes; ep.HandshakeAuth is populated with the client's auth payload (raw JSON, nil if not provided)
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

Custom events map directly to the event name used in socket.emit("myEvent", …) on the client and kws.EmitEvent("myEvent", data) on the server.

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]any Optional websocket attributes
Error error (optional) Fired from disconnection or error events
Data []byte Raw JSON of the event payload (first argument of socket.emit)
Args [][]byte All raw JSON arguments after the event name; useful when the client emits multiple values
AckID uint64 Ack id assigned by the client when it emitted with a callback (0 if HasAck is false)
HasAck bool True when the inbound event expects an ack reply; respond via EventPayload.Ack(args...)
HandshakeAuth json.RawMessage Raw JSON auth payload from the Socket.IO handshake; populated on EventConnect listeners (use Kws.HandshakeAuth() elsewhere)

Socket instance functions

Name Type Description
SetAttribute void Set a specific attribute for the specific socket connection
GetUUID string Get 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
Broadcast void Broadcast to all the active connections except broadcasting the message to itself
Fire void Fire custom event
Emit void Send data as a "message" socket.io event; valid JSON is passed through, raw text is JSON-encoded
EmitEvent void Send a named socket.io event; valid JSON is passed through, raw text is JSON-encoded
EmitArgs void Emit a named event with multiple arguments; valid JSON is passed through, raw text is JSON-encoded
EmitWithAck void Emit an event and invoke cb(ack) when the client acks (uses OutboundAckTimeout)
EmitWithAckTimeout void Like EmitWithAck but with a per-call timeout and a structured AckCallback
EmitWithAckArgs void Multi-arg variant; cb([][]byte, error) receives the ack tuple (uses OutboundAckTimeout)
HandshakeAuth json.RawMessage Raw JSON auth payload sent by the client at connect time (nil if absent)
IsAlive bool Reports whether the underlying connection is still open and the heartbeat loop is running
IsPolling bool Reports whether the session is bound to HTTP long-polling rather than WebSocket; when true, Conn is nil
Close void Actively close the connection from the server

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

kws.Conn

kws.Conn is nil for HTTP long-polling sessions. Code that touches the underlying WebSocket directly should guard with if kws.Conn != nil or check the transport via the absence of kws.Conn. Listener APIs (Emit, Ack, Close, Broadcast, EmitWithAck, etc.) work transparently on both transports.

Documentation

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

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 that is
	// not bound to a named Socket.IO event (i.e. wire-level "message" events
	// or raw binary frames).
	EventMessage = "message"
	// EventPing is fired when a WebSocket PING control frame is received
	// from the peer. See
	// https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets
	EventPing = "ping"
	// EventPong is fired when a WebSocket PONG control frame, or an
	// Engine.IO PONG packet, is received from the peer.
	EventPong = "pong"
	// EventDisconnect is fired exactly once when the connection is torn
	// down, regardless of which side initiated the close. The
	// EventPayload.Error field carries the close reason (RFC 6455 section
	// 11.7) when available, or nil for a clean shutdown.
	EventDisconnect = "disconnect"
	// EventConnect is fired exactly once after the Engine.IO and Socket.IO
	// handshake completes, before the read loop starts dispatching events.
	// EventPayload.HandshakeAuth carries the client's auth payload, if any.
	EventConnect = "connect"
	// EventClose is fired exactly once when the connection is closed from
	// the server side via Websocket.Close.
	EventClose = "close"
	// EventError is fired when an error occurs on the connection (read,
	// write, parse, or listener panic). EventPayload.Error carries the
	// error value.
	EventError = "error"
)

Supported event list

Variables

View Source
var (
	// ErrorInvalidConnection is returned when the addressed Conn connection is no
	// longer available; the error data carries the UUID of that connection.
	ErrorInvalidConnection = errors.New("message cannot be delivered invalid/gone connection")
	// ErrorUUIDDuplication is returned when the requested UUID already exists in
	// the active connections pool.
	ErrorUUIDDuplication = errors.New("UUID already exists in the available connections pool")
	// ErrAckNotRequested is returned by EventPayload.Ack when the event did
	// not include an ack id (i.e. the client emitted without a callback).
	ErrAckNotRequested = errors.New("socketio: event has no ack id")
	// ErrAckAlreadySent is returned by EventPayload.Ack when Ack() has
	// already been called once for this payload.
	ErrAckAlreadySent = errors.New("socketio: ack already sent")
	// ErrAckTimeout is delivered to outbound ack callbacks (registered via
	// EmitWithAckTimeout) when the client does not respond within the
	// configured timeout.
	ErrAckTimeout = errors.New("socketio: ack timeout")
	// ErrAckDisconnected is delivered to outbound ack callbacks when the
	// connection is torn down before an ack arrives.
	ErrAckDisconnected = errors.New("socketio: connection closed before ack")
	// ErrReservedEventName is surfaced via EventError when user code tries
	// to emit a name the JS socket.io client treats as a reserved
	// lifecycle event (e.g. "connect", "disconnect"). The emit is dropped
	// before the frame reaches the wire.
	ErrReservedEventName = errors.New("socketio: reserved event name cannot be emitted")
	// ErrAckIDOverflow is returned from inbound SIO frame parsing when the
	// numeric ack id prefix would overflow uint64. Surfaced via EventError.
	ErrAckIDOverflow = errors.New("socketio: ack id overflow")
	// ErrEmptyEventArray is returned from inbound SIO EVENT parsing when
	// the JSON-array payload is empty (no event name). Surfaced via
	// EventError.
	ErrEmptyEventArray = errors.New("socketio: empty event array")
	// ErrHandshakeClosed is returned from the EIO/SIO handshake when the
	// peer closes the connection before completing the SIO CONNECT step.
	ErrHandshakeClosed = errors.New("socketio: connection closed during handshake")
	// ErrInvalidNamespace is returned from the SIO CONNECT handshake when
	// the namespace prefix fails charset validation. The handshake is
	// rejected with CONNECT_ERROR before any user code runs.
	ErrInvalidNamespace = errors.New("socketio: invalid namespace in SIO CONNECT")
	// ErrInvalidAuthPayload is returned from the SIO CONNECT handshake
	// when the auth field is malformed JSON, exceeds MaxAuthPayload, or
	// is not a JSON object per socket.io-protocol v5.
	ErrInvalidAuthPayload = errors.New("socketio: invalid auth payload in SIO CONNECT")
	// ErrHeartbeatTimeout is the disconnection cause delivered to
	// EventDisconnect when no client PONG arrives within
	// PingInterval + PingTimeout.
	ErrHeartbeatTimeout = errors.New("socketio: heartbeat timeout")
	// ErrSendQueueClosed is the disconnection cause delivered to
	// EventDisconnect when the send queue is full and DropFramesOnOverflow
	// is false (the legacy hard-teardown path). Compare against
	// ErrSendQueueOverflow which fires on per-frame drops when
	// DropFramesOnOverflow is true.
	ErrSendQueueClosed = errors.New("socketio: send queue overflow")
	// ErrBatchPacketsExceeded is surfaced via EventError when a batched
	// EIO frame contains more than MaxBatchPackets record-separated
	// packets. Remaining packets in the frame are dropped.
	ErrBatchPacketsExceeded = errors.New("socketio: batched frame exceeds MaxBatchPackets")
	// ErrPollingBodyTooLarge is delivered to EventDisconnect when an
	// inbound polling POST exceeds PollingMaxBufferSize. The session
	// is torn down to mirror the WebSocket SetReadLimit behaviour;
	// repeated oversized POSTs would otherwise keep sessions alive
	// until heartbeat reaped them.
	ErrPollingBodyTooLarge = errors.New("socketio: polling POST body exceeds PollingMaxBufferSize")
	// ErrUnknownEIOPacket is surfaced via EventError when the inbound EIO
	// packet type byte does not match any recognised Engine.IO opcode.
	ErrUnknownEIOPacket = errors.New("socketio: unknown EIO packet type")
)
View Source
var (
	// PongTimeout is the legacy heartbeat timeout knob.
	//
	// Deprecated: PongTimeout is no longer consulted by the heartbeat
	// implementation. Use PingTimeout instead.
	PongTimeout = 20 * time.Second
	// RetrySendTimeout is the back-off delay the send goroutine waits
	// before retrying a failed write to a temporarily unavailable
	// connection.
	RetrySendTimeout = 20 * time.Millisecond
	// MaxSendRetry is the maximum number of times the send goroutine
	// retries a frame against a missing connection before dropping it.
	MaxSendRetry = 5
	// ReadTimeout is no longer consulted by the read loop, which now
	// blocks in a single ReadMessage call gated by SetReadDeadline rather
	// than busy-polling with a sleep.
	//
	// Deprecated: kept only for backward compatibility with code that
	// still references the variable; setting it has no effect.
	ReadTimeout = 10 * time.Millisecond
	// PingInterval is how often the server sends Engine.IO PING packets
	// to the client. The client must reply with a PONG packet to keep
	// the connection alive. Read once per connection at handshake time;
	// mutating it does not affect already-open sockets.
	PingInterval = 25 * time.Second
	// PingTimeout is advertised to the client in the EIO OPEN packet and
	// is also used by the server-side heartbeat enforcer: a connection
	// is dropped when no frame arrives within PingInterval + PingTimeout.
	// Read once per connection at handshake time.
	PingTimeout = 20 * time.Second
	// HandshakeTimeout caps how long the server waits for the client SIO
	// CONNECT packet ("40") after sending the EIO OPEN packet. Set to
	// zero to disable.
	HandshakeTimeout = 10 * time.Second
	// MaxPayload is the maximum size in bytes of an inbound WebSocket
	// frame. It is advertised to the client in the EIO OPEN packet and
	// enforced via SetReadLimit on the underlying connection: frames
	// exceeding this size are rejected and the connection is closed. Set
	// to zero or negative to disable the limit (not recommended).
	MaxPayload int64 = 1_000_000
	// MaxAuthPayload is the maximum size in bytes of the JSON auth
	// payload supplied by the client in the SIO CONNECT ("40") packet.
	// Per socket.io-protocol v5 the auth field is a JSON object (or
	// absent); any payload exceeding this cap is rejected with
	// CONNECT_ERROR and the handshake fails. Defaults to 8 KiB. Set to
	// zero to disable.
	MaxAuthPayload int = 8 * 1024
	// OutboundAckTimeout is the default deadline for ack callbacks
	// registered via Websocket.EmitWithAck and Websocket.EmitWithAckArgs.
	// Use Websocket.EmitWithAckTimeout for per-call overrides.
	OutboundAckTimeout = 30 * time.Second
	// MaxBatchPackets caps the number of EIO packets accepted in a
	// single 0x1E-batched WebSocket frame. Without this cap, a frame
	// consisting almost entirely of record separators forces a
	// multi-megabyte slice header allocation. 256 is comfortably above
	// any legitimate batch.
	MaxBatchPackets = 256
	// MaxEventNameLength bounds inbound SIO event name strings (in
	// bytes) so a hostile client cannot pin a multi-megabyte string per
	// frame inside the EventPayload dispatched to user listeners. Set
	// to zero to disable the bound (not recommended).
	MaxEventNameLength = 256
	// SendQueueSize is the buffered capacity of the per-connection
	// outbound frame queue. Tune it before connections are accepted;
	// existing sockets retain the size in effect at New() time.
	SendQueueSize = 100
	// DropFramesOnOverflow controls behavior when the per-connection
	// send queue is full. When false (default) the connection is torn
	// down with a "send queue overflow" error (legacy behavior); when
	// true the individual frame is dropped and EventError fires with
	// ErrSendQueueOverflow, allowing the connection to survive bursty
	// producers.
	DropFramesOnOverflow = false
	// ErrSendQueueOverflow is surfaced via EventError when a frame is
	// dropped because the send queue was full and DropFramesOnOverflow
	// is true.
	ErrSendQueueOverflow = errors.New("socketio: send queue overflow, frame dropped")
)

Tunable package-level knobs. Mutate them before calling New so each new connection captures the desired value; in-flight connections retain the values in effect at handshake time and are not affected by later changes. These vars are not safe for concurrent mutation once connections are open.

View Source
var EnablePolling = false

EnablePolling toggles HTTP long-polling fallback support. When true, the handler returned from New() also accepts Engine.IO v4 polling requests (transport=polling) on the same route, in addition to the existing WebSocket upgrade. Default false preserves prior behavior.

Read once per request; mutate before serving connections.

To enable polling on the route, the user must mount the handler for both GET and POST methods, e.g.

h := socketio.New(cb)
app.Get("/socket.io/", h)
app.Post("/socket.io/", h)

or app.All("/socket.io/", h).

Polling sessions advertise an empty "upgrades" array in the Engine.IO OPEN packet: clients connected via polling stay on polling for the session lifetime. (Polling-to-WebSocket transport upgrade is not yet implemented; see issue tracker.)

View Source
var Logger func(level, msg string, fields ...any)

Logger is an optional package-level hook that, when non-nil, receives every internal warning/error the socketio package emits. The default (nil) preserves the historical "silent" behavior, so this is a non- breaking addition.

level is one of "warn" or "error". msg is a short, stable description of the event class (e.g. "handshake_failure", "queue_overflow", "ack_timeout"). fields is a flat key/value list of structured context suitable for forwarding to slog/zap/zerolog/etc., e.g.

Logger = func(level, msg string, fields ...any) {
    slog.Default().Log(context.Background(), levelOf(level), msg, fields...)
}

Implementations MUST be safe for concurrent use and MUST NOT block; the hook is invoked from goroutines on the connection hot path.

Logger is read without synchronisation; assign it once during process startup before serving connections.

View Source
var MaxPollWait = 30 * time.Second

MaxPollWait caps how long a long-poll GET blocks waiting for outbound frames. The pong heartbeat (PingInterval, default 25s) typically enqueues a "2" PING packet within MaxPollWait, so most polls return well before this deadline. Set to zero to disable (not recommended).

View Source
var PollQueueMaxFrames = 1024

PollQueueMaxFrames caps the number of frames buffered in a polling session's outbound queue. When the cap is hit, behaviour mirrors the WebSocket SendQueueSize backpressure: with DropFramesOnOverflow=true the new frame is dropped and EventError fires with ErrSendQueueOverflow; otherwise the session is torn down with ErrSendQueueClosed. Without this cap, a slow or absent poller paired with a steady server-side emit stream would grow the queue without bound. Set to zero to disable the cap (not recommended).

View Source
var PollingMaxBufferSize = 1_000_000

PollingMaxBufferSize bounds the size of a single polling HTTP body (POST request body and GET long-poll response body) in bytes. Matches engine.io's maxHttpBufferSize default of 1 MB.

Functions

func Broadcast

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

Broadcast is the package-level form of Websocket.Broadcast. It sends message to every active connection in the pool, including the originator (use the method form to skip the originator). See Websocket.Emit for the meaning of mType.

func EmitTo

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

EmitTo is the package-level form of Websocket.EmitTo. It sends message to the connection identified by uuid and returns ErrorInvalidConnection when the target is unknown or already closed.

func EmitToList

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

EmitToList is the package-level form of Websocket.EmitToList. It sends message to every connection whose UUID appears in uuids. Errors are silently ignored; use the method form to receive them via EventError.

func Fire

func Fire(event string, data []byte)

Fire delivers a synthetic event to the listeners registered for event on every active connection. It does not produce a wire frame. See Websocket.Fire for the per-connection variant.

func New

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

New returns a Fiber handler that upgrades the request to a Socket.IO- compatible WebSocket, performs the Engine.IO / Socket.IO handshake, and invokes callback with the established Websocket so user code can register per-connection state before the read and heartbeat goroutines start.

Before delegating to the WebSocket upgrader, the handler validates the Engine.IO protocol version supplied via the "EIO" query parameter. Only EIO v4 is supported; an empty value defaults to v4. Any other value is rejected with HTTP 400 and a JSON error body matching the reference socket.io server response, so older clients (e.g. EIO v3) surface a recognisable, transport-level handshake error instead of being silently upgraded into an incompatible session.

func On

func On(event string, callback eventCallback)

On registers callback for the named event. The callback fires for every connection that receives event, in registration order. Multiple callbacks may be registered for the same event; all run synchronously on the connection's read goroutine.

On is concurrency-safe and may be called at any time, including from inside another listener; later registrations may or may not be observed by concurrent dispatch loops (eventual consistency). Listeners cannot be unregistered. A listener that calls payload.Ack must do so synchronously or pass the payload to a goroutine that copies any byte slices it needs.

func Shutdown added in v1.2.0

func Shutdown(ctx context.Context) error

Shutdown closes every active socket.io connection in the pool and waits for all of their per-connection goroutines (send, read, pong) to exit, or until ctx is cancelled.

Wire this into fiber.App.Shutdown / fiber.App.ShutdownWithContext so an application shutdown deterministically tears down sockets instead of relying on the framework to close the underlying transport.

Returns ctx.Err() when ctx is cancelled before all connections finished draining; otherwise returns nil.

Types

type AckCallback added in v1.2.0

type AckCallback func(ack []byte, err error)

AckCallback receives the result of a server-initiated EmitWithAck.

On a successful ack, ack holds the raw JSON of the client's FIRST ack argument (or nil when the client acked with no arguments) and err is nil. Multi-argument acks are not surfaced through this single-arg shape; use EmitWithAckArgs when the client may ack with more than one argument or when "single arg that is itself a JSON array" must be distinguished from "multiple args".

On timeout err is ErrAckTimeout. On connection close err is ErrAckDisconnected. In both error cases ack is nil.

type EventPayload

type EventPayload struct {
	// Kws is the connection that fired the event. Use it to call
	// Emit/EmitEvent/Close from inside the listener.
	Kws *Websocket
	// Name is the event name as registered with On (e.g. "message",
	// EventConnect, or any custom event).
	Name string
	// SocketUUID is the unique identifier of the originating connection,
	// captured at dispatch time so it remains stable even if the
	// connection is concurrently closed.
	SocketUUID string
	// SocketAttributes is a defensive snapshot of the connection's
	// attribute map taken at dispatch time. Mutating it does not affect
	// the live connection; use Kws.SetAttribute for that.
	SocketAttributes map[string]any
	// Error is the cause associated with lifecycle events such as
	// EventDisconnect and EventError; nil for ordinary user events.
	Error error
	// Data is the first event argument (if any). Kept for backwards
	// compatibility; equivalent to Args[0] when len(Args) > 0, else nil.
	Data []byte
	// Args are the raw-JSON arguments the client sent with the event.
	// Each entry is one JSON value; nil for events without args. Use
	// this to consume socket.emit("event", a, b, c) from the JS client.
	Args [][]byte
	// AckID is the Socket.IO ack id attached to this event by the
	// client. It is meaningful only when HasAck is true. Use Ack to
	// respond.
	AckID uint64
	// HasAck reports whether the client requested an ack for this
	// event (i.e. the JS side called socket.emit("event", data,
	// callback)).
	HasAck bool
	// HandshakeAuth is the raw JSON auth payload the client supplied
	// in its SIO CONNECT packet, copied for safety. nil if the client
	// connected without an auth payload. Populated for EventConnect
	// listeners; for other events use Kws.HandshakeAuth instead.
	HandshakeAuth json.RawMessage
	// contains filtered or unexported fields
}

EventPayload is the read-only value passed to every event listener. It carries the originating connection (Kws), the event arguments (Args, with Data as a shortcut to the first arg), ack bookkeeping (AckID, HasAck, Ack) and the handshake auth payload (HandshakeAuth, populated for EventConnect listeners only).

Byte-slice fields (Args, Data, HandshakeAuth) carry their own backing storage independent of the read buffer: parseSIOEvent copies inbound args, the read goroutine copies binary frames before dispatch, and HandshakeAuth is captured during the handshake. Listeners may safely retain these slices across goroutine boundaries.

func (*EventPayload) Ack added in v1.2.0

func (ep *EventPayload) Ack(args ...[]byte) error

Ack sends a Socket.IO ACK ("43") response back to the client for the event represented by this payload. The variadic args ...[]byte signature accepts zero arguments (empty ack), one argument (single value), or many arguments that are emitted as comma-separated values, mirroring the JS-side callback(a, b, c) shape. Valid JSON args are passed through; raw text args are encoded as JSON strings. Nil or empty entries are skipped.

Ack is idempotent across all listeners dispatched for the same inbound event: only the first invocation produces a wire frame, and subsequent calls (whether on the same payload or on a sibling payload handed to another listener) return ErrAckAlreadySent.

Returns an error if the event has no ack id, the connection is closed, or the ack has already been sent for this payload.

type Websocket

type Websocket struct {

	// Conn is the underlying Fiber WebSocket connection. Treat it as
	// read-only from listener callbacks; writes must go through the
	// Emit/EmitEvent/Broadcast methods so the send goroutine remains
	// the sole writer.
	Conn *websocket.Conn

	// UUID is the unique identifier assigned to this connection and used as
	// its key in the active connections pool.
	UUID string
	// Locals wraps the Fiber Locals lookup so listener callbacks can reach
	// values stored on the originating request context.
	Locals func(key string) interface{}
	// Params wraps the Fiber Params lookup so listener callbacks can read
	// route parameters from the originating request.
	Params func(key string, defaultValue ...string) string
	// Query wraps the Fiber Query lookup so listener callbacks can read
	// query-string values from the originating request.
	Query func(key string, defaultValue ...string) string
	// Cookies wraps the Fiber Cookies lookup so listener callbacks can read
	// cookie values from the originating request.
	Cookies func(key string, defaultValue ...string) string
	// contains filtered or unexported fields
}

Websocket represents a single Socket.IO connection on top of the underlying Fiber WebSocket. It carries the per-connection state (UUID, namespace, attributes, ack bookkeeping) and exposes the Emit/Broadcast/Close API that user code interacts with from inside listener callbacks.

func (*Websocket) Broadcast

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

Broadcast sends message to every active connection in the pool. When except is true the originating connection is skipped. The optional mType selects the WebSocket frame type: omit it (or pass TextMessage) to wrap message as a Socket.IO "message" event; pass BinaryMessage to send the bytes verbatim as a binary frame.

func (*Websocket) Close

func (kws *Websocket) Close()

Close actively closes the connection from the server side.

It is idempotent: the synchronous DISCONNECT plus close-frame write block runs at most once even when called concurrently. EventClose fires exactly once before the regular disconnected tear-down (which fires EventDisconnect). Callers may invoke Close from inside an event listener; it does not block on the listener's own goroutine.

Synchronous writes (bypassing kws.queue) are required so the frames reach the wire BEFORE disconnected() closes done and the send goroutine shuts down. The kws.mu write lock serialises with the send goroutine (which writes under kws.mu.RLock) so the Conn is never written concurrently.

closeOnce gates the actual write block so concurrent Close() callers do not double-write the disconnect frames and, more importantly, do not race the upgrade handler's deferred releaseConn() that nils the embedded *fasthttp.Conn the moment run() returns. The companion kws.mu.Lock()/Unlock() fence at the tail of run() guarantees an in-flight Close() write has completed before the handler returns and releaseConn() fires.

func (*Websocket) Emit

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

Emit sends message to the client wrapped as a Socket.IO "message" event (use EmitEvent for named events). The message bytes may be valid JSON (object, array, string literal, number, etc.) or raw text; raw text is encoded as a JSON string so socket.io-client can parse the frame.

The optional mType selects the WebSocket frame type: omit it (or pass TextMessage) for a Socket.IO "message" event; pass BinaryMessage to send the bytes verbatim as a binary WebSocket frame. The connection's namespace (captured during the handshake) is mirrored on the wire.

Concurrency-safe: enqueues onto the per-connection send queue. Calls on already-disconnected sockets are a no-op. Behavior on a full queue is governed by DropFramesOnOverflow.

func (*Websocket) EmitArgs added in v1.2.0

func (kws *Websocket) EmitArgs(event string, args ...[]byte)

EmitArgs sends a named socket.io event with multiple arguments, matching the JS-side call socket.emit("event", a, b, c). Valid JSON args are passed through; raw text args are encoded as JSON strings. Empty entries are skipped. The connection's namespace (captured during the handshake) is mirrored on the wire. Reserved event names are rejected as in EmitEvent. Concurrency-safe.

func (*Websocket) EmitEvent added in v1.2.0

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

EmitEvent sends a named socket.io event to the client. The data parameter may be valid JSON or raw text. The connection's namespace (captured during the handshake) is mirrored on the wire.

Reserved event names ("connect", "connect_error", "disconnect") are rejected: the call is dropped and EventError fires with ErrReservedEventName instead. Concurrency-safe.

func (*Websocket) EmitTo

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

EmitTo sends message to the connection identified by uuid. Returns the underlying pool error (typically ErrorInvalidConnection) when the target is unknown or already closed; in either case an EventError is fired on kws so fan-out callers (EmitToList, Broadcast) do not have to re-fire it. See Websocket.Emit for the meaning of mType.

func (*Websocket) EmitToList

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

EmitToList sends message to every connection whose UUID appears in uuids. Per-target failures are surfaced as EventError on kws by EmitTo (not returned). See Websocket.Emit for the meaning of mType.

func (*Websocket) EmitWithAck added in v1.2.0

func (kws *Websocket) EmitWithAck(event string, data []byte, cb func(ack []byte))

EmitWithAck sends a named socket.io event and registers a callback that is invoked exactly once with the client's ack response. The connection's namespace (captured during the handshake) is mirrored on the wire.

The data parameter may be valid JSON or raw text. The callback receives the raw JSON bytes from the client's ack ([] for an empty ack, the first arg as JSON, or a JSON-array when the client called the ack with multiple args).

On a healthy round-trip the callback fires with the ack bytes. If the client does not respond within OutboundAckTimeout the callback fires with nil. If the connection closes before any ack arrives the callback fires with nil. Because both error paths surface as a nil ack, callers that need to distinguish "client never replied" from "connection torn down" should use EmitWithAckTimeout, which delivers ErrAckTimeout versus ErrAckDisconnected through its structured AckCallback.

func (*Websocket) EmitWithAckArgs added in v1.2.0

func (kws *Websocket) EmitWithAckArgs(event string, args [][]byte, cb func([][]byte, error))

EmitWithAckArgs is the multi-arg + structured-error variant of EmitWithAck. It sends a named event carrying multiple arguments and registers a callback invoked exactly once when the client acks, on timeout (OutboundAckTimeout), or on connection close. The connection's namespace (captured during the handshake) is mirrored on the wire.

On success cb is called with (args, nil) where args is the slice of raw-JSON ack arguments the client sent. On timeout cb is called with (nil, ErrAckTimeout); on disconnect (nil, ErrAckDisconnected).

func (*Websocket) EmitWithAckTimeout added in v1.2.0

func (kws *Websocket) EmitWithAckTimeout(event string, data []byte, timeout time.Duration, cb AckCallback)

EmitWithAckTimeout is the timeout-aware variant of EmitWithAck. Pass timeout = 0 to disable the timeout (the callback only fires when the client acks or the connection closes). The connection's namespace (captured during the handshake) is mirrored on the wire.

The callback's err is one of: nil (ack received), ErrAckTimeout, or ErrAckDisconnected.

func (*Websocket) Fire

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

Fire delivers a synthetic event to the listeners registered for event on this connection only. It does not produce a wire frame; use it to inject internal events from server-side code. The data slice is exposed as the listener's EventPayload.Data and as Args[0].

func (*Websocket) GetAttribute

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

GetAttribute returns the per-connection attribute previously stored under key, or nil if no such attribute exists. Concurrency-safe.

func (*Websocket) GetIntAttribute

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

GetIntAttribute returns the per-connection attribute under key as an int, or 0 if no such attribute exists. Panics if the stored value is not an int.

func (*Websocket) GetStringAttribute

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

GetStringAttribute returns the per-connection attribute under key as a string, or "" if no such attribute exists. Panics if the stored value is not a string.

func (*Websocket) GetUUID

func (kws *Websocket) GetUUID() string

GetUUID returns the unique identifier of this connection in a concurrency-safe manner.

func (*Websocket) HandshakeAuth added in v1.2.0

func (kws *Websocket) HandshakeAuth() json.RawMessage

HandshakeAuth returns the raw JSON auth payload supplied by the client in its SIO CONNECT packet (e.g. `{"token":"..."}`). It returns nil for clients that did not send an auth payload.

Typical use: validate the auth payload from inside the New() callback or the EventConnect listener, then call kws.Close() if invalid.

func (*Websocket) IsAlive

func (kws *Websocket) IsAlive() bool

IsAlive reports whether the connection is still considered active and able to deliver outbound frames. Lock-free.

func (*Websocket) IsPolling added in v1.2.0

func (kws *Websocket) IsPolling() bool

IsPolling reports whether this session is bound to the HTTP long- polling transport rather than to a WebSocket. When true, kws.Conn is nil; user code that touches kws.Conn directly must guard with this check (or just use the transport-agnostic Emit / Broadcast / Ack / Close API, which works on both transports).

func (*Websocket) SetAttribute

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

SetAttribute stores a per-connection key/value pair. Concurrency-safe. Listeners receive a defensive snapshot via EventPayload.SocketAttributes.

func (*Websocket) SetUUID

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

SetUUID replaces this connection's UUID, updating the active connections pool atomically. It returns ErrorUUIDDuplication when the requested UUID is already taken by another live connection.

Directories

Path Synopsis
example
auth-and-ack command
Example: Socket.IO server with handshake auth, listener-side payload.Ack, server-initiated EmitWithAckTimeout, and graceful Shutdown(ctx) on SIGINT/SIGTERM.
Example: Socket.IO server with handshake auth, listener-side payload.Ack, server-initiated EmitWithAckTimeout, and graceful Shutdown(ctx) on SIGINT/SIGTERM.

Jump to

Keyboard shortcuts

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