Socket

The file websocket/index.gox defines the main behavior of each WebSocket connection accepted by gox-server.

Your function Index receives an instance of gox.Socket and records the events that will use the connection.

gox
module websocket

import "github.com/gox-lang/gox"

func Index(socket *gox.Socket) gox.WebSocket {
    socket.When("connect", func(isReconnect bool) {
        socket.Emit("welcome", "Hola, George")
    })

    socket.On("message", func(message string) {
        socket.Emit("message", message)
    })
}

Each connected client receives its own socket. The instance remains available throughout the life of the connection.


The index.gox file

The main entry point should be found at:

text
my-project/
└── websocket/
    └── index.gox

The file uses the module websocket:

gox
module websocket

The input function should be called Index and respect this signature:

gox
func Index(socket *gox.Socket) gox.WebSocket

Index It runs when the client tries to establish the connection, before completing the handshake.

Its main purpose is to record:

  • Handshake validation.
  • Life cycle events.
  • Communication events.
  • Initial socket behavior.
gox
func Index(socket *gox.Socket) gox.WebSocket {
    socket.When("handshake", func(ctx *gox.Context) bool {
        return true
    })

    socket.When("connect", func(isReconnect bool) {
        socket.Emit("welcome")
    })

    socket.On("message", func(message string) {
        socket.Emit("message", message)
    })
}

It is not necessary to create, record or keep the requested instance manually. GOX manages its life cycle.


Structure

gox.Socket provides the following fields and methods to manage a connection:

text
Socket
{
    Session gox.Session
    Handshake gox.SocketHandshake
    Siblings gox.SocketCollection

    ID() string
    IsLocal() bool
    InstanceID() string
    When(event string, handler any)
    On(event string, handler any)
    Emit(event string, values ...any)
    Disconnect(code gox.WSCloseCode)
    Join(group string) error
    Leave(group string) error
    LeaveAll() error
    Groups() []string
    EmitTo(group string, event string, values ...any)
}

Some methods are part of specific WebSocket areas:

APIDocumentation
When()Lifecycle
On() and Emit()Events
Emit(...).Await()Requests
Join(), Leave(), LeaveAll(), Groups() and EmitTo()Groups

Fields

FieldDescription
SessionIt provides access to the associated session.
HandshakeKeep the data from the request that started the connection.
SiblingsIt represents the other sockets of the same session.

Session

Tipo:gox.Session

Session contains the same session used by the HTTP request that started the handshake.

gox
var userID string

exists, err := socket.Session.Get("userID", &userID)
if err != nil {
    socket.Disconnect(gox.WSCloseInternal)
}

The values stored from a Page or Action are available on WebSocket.

gox
socket.On("updateName", func(name string) {
    if err := socket.Session.Set("name", name); err != nil {
        socket.Disconnect(gox.WSCloseInternal)
    }
})

The session remains active as the socket exchanges events. GOX continues its changes and automatically renews its activity.

When the session expires or is destroyed, the associated connections are closed with the corresponding code.

Rotate() is not available from WebSocket because the rotation needs an HTTP response to send the new token to the client.

See Sessions to know the storage, TTL and the life cycle of the sessions.


Handshake

Tipo:gox.SocketHandshake

Handshake contains an immutable copy of the HTTP request that started the connection.

text
SocketHandshake
{
    URL gox.URL
    Headers gox.Collection[string]
    Cookies gox.Collection[string]
    Query gox.Collection[string]

    Method() string
    IP() string
    IsSecure() bool
    Origin() string
    UserAgent() string
}

The snapshot is created before running Index and remains available throughout the connection.

gox
origin := socket.Handshake.Origin()
userAgent := socket.Handshake.UserAgent()

It also allows you to check the URL, headers, cookies and query parameters received:

gox
room, exists := socket.Handshake.Query.Get("room")
gox
authorization, exists := socket.Handshake.Headers.Get("Authorization")

Its methods provide direct access to the main data:

MethodDescription
Method()Returns the HTTP method used.
IP()Returns the IP address resolved for the client.
IsSecure()It indicates whether the connection uses safe transport.
Origin()Returns the header value Origin.
UserAgent()Returns the client's user agent.

Handshake only contains information that can remain valid after the HTTP request is completed. Context, Body, Form, Files, Writer or Response.

During the event handshake, GOX also delivers the original HTTP context to authorize or reject the connection. Do not keep that context after the handler is finished.

See Request to know the structure of the original HTTP request.


Siblings

Tipo:gox.SocketCollection

Siblings represents the other active sockets that belong to the same session.

The current socket is never part of the collection.

text
SocketCollection
{
    All() []*gox.Socket
    Count() int
    Emit(event string, values ...any)
    Disconnect(code gox.WSCloseCode)
}

The collection is dynamic. Each operation uses the sockets that remain connected at that time.

gox
connectedDevices := socket.Siblings.Count()

All() returns a snapshot of the current sockets:

gox
for _, sibling := range socket.Siblings.All() {
    log.Printf("Socket relacionado: %s", sibling.ID())
}

Emit() send an event to all the other sockets of the session:

gox
socket.Siblings.Emit("profileUpdated")

Disconnect() close all sockets included in the collection:

gox
socket.Siblings.Disconnect(gox.WSClosePermanent)

When the cluster is active, Siblings also includes the connections of the same session held by other gox-server. Emission and disconnection operations are automatically distributed.

During Index and handshakeThe connection hasn't been registered yet. Siblings from connect or from subsequent events.


Methods

MethodDescription
ID()Returns the connection identifier.
IsLocal()It indicates whether the connection belongs to the current instance.
InstanceID()Identify the instance that keeps the connection.
When()He records a life cycle event.
On()He records a communication event.
Emit()Send an event to the client.
Disconnect()Close the connection.
Join()Add the socket to a group.
Leave()It removes the socket from a group.
LeaveAll()It eliminates the socket of all its groups.
Groups()Returns the current socket groups.
EmitTo()Send an event to the members of a group.

ID()

Firma:func (*Socket) ID() string

ID() returns the unique identifier of the connection.

gox
socketID := socket.ID()

GOX assigns the identifier after correctly completing the WebSocket upgrade.

The value remains the same throughout the socket's life and is not reused when the client reconnects.

During the initial execution of Index and the event handshake, the socket has not yet been registered and ID() return an empty string. Use it from connect or from subsequent events.

gox
socket.When("connect", func(isReconnect bool) {
    log.Printf("Socket conectado: %s", socket.ID())
})

Each connection receives a different identifier, even when several connections belong to the same session.

The identifier is administered by GOX and cannot be modified by public API.


Location

Methods IsLocal() e InstanceID() allow you to know where a connection is physically located.

IsLocal()

Firma:func (*Socket) IsLocal() bool

IsLocal() indicates whether the socket belongs to the current instance of gox-server.

gox
if socket.IsLocal() {
    log.Printf("Conexión local: %s", socket.ID())
}

Returns true for a connection administered by the current instance and false for a remote representation obtained by the cluster.

InstanceID()

Firma:func (*Socket) InstanceID() string

InstanceID() returns the instance identifier of the gox-server that keeps the connection.

gox
instanceID := socket.InstanceID()

The location is assigned after completing the handshake and remains the same throughout the socket's life.

These methods are mainly useful when the application needs to distinguish local and distributed connections. Emission, disconnection, groups and channels do not require manually checking the location.

See Cluster configuration to know how to distribute WebSocket and sessions between various instances.


Event registration

When() records events related to the connection's life cycle.

gox
socket.When("connect", func(isReconnect bool) {
    socket.Emit("welcome")
})

On() records events sent by the client.

gox
socket.On("greeting", func(name string) {
    socket.Emit("greeting", "Hola, "+name)
})

The handlers are registered by name. Recording the same name replaces the previous handler in that category.

Life cycle events and communication events use independent records.

See Lifecycle to know the events available by When() and Events to learn how to use On() and Emit().


Emit()

Firma:func (*Socket) Emit(event string, values ...any)

Emit() send an event to the connected client.

gox
socket.Emit("welcome", "Hola, George")

You can send one or more values:

gox
socket.Emit("profileUpdated", "George", true)

The values must be compatible with the GOX WebSocket protocol.

Emissions made before the transport is prepared are kept in order and sent when the connection is available.

Emit() does not return an error. If GOX cannot encode or send the event, deliver the problem by the event error From the socket.

See Events to know the communication in both directions and Values to know the types that can be transferred.


Disconnect()

Firma:func (*Socket) Disconnect(code gox.WSCloseCode)

Disconnect() Close the connection using a WebSocket closing code.

gox
socket.Disconnect(gox.WSCloseNormal)

GOX automatically uses the description associated with the code.

gox
socket.Disconnect(gox.WSClosePermanent)

The code gox.WSCloseDefault equivalent to gox.WSCloseNormal.

Disconnection can enable or prevent client reconnection according to the code used. Lifecycle to know the codes, the causes of closure and the reconnection behavior.


Full example

The following example validates the handshake, uses the session and records events for the connection:

gox
module websocket

import "github.com/gox-lang/gox"

func Index(socket *gox.Socket) gox.WebSocket {
    socket.When("handshake", func(ctx *gox.Context) bool {
        return socket.Handshake.Origin() == "https://example.com"
    })

    socket.When("connect", func(isReconnect bool) {
        var name string

        exists, err := socket.Session.Get("name", &name)
        if err != nil {
            socket.Disconnect(gox.WSCloseInternal)
            return
        }

        if !exists {
            name = "George"
        }

        socket.Emit("welcome", name, isReconnect)
        socket.Siblings.Emit("deviceConnected", socket.ID())
    })

    socket.On("message", func(message string) {
        socket.Emit("message", message)
        socket.Siblings.Emit("message", message)
    })
}

Index records the behavior once for each connection attempt. Registered handlers continue to use the same instance of gox.Socket.


Best practices

  • Use Index to register handlers and delegate business logic to specialized functions or packages.
  • Use ID() only after completing the handshake.
  • Do not manually modify the fields administered by GOX.
  • Do not keep the context received by the event handshake.
  • Check the errors made when reading or modifying the session.
  • Do not use Rotate() from WebSocket.
  • Use Siblings to communicate with the other connections of the same session.
  • It allows GOX to distribute operations without manually checking the location of the socket.
  • It uses closing codes that correctly represent the cause of the disconnection.
  • Do not send information that the current session is not authorized to consult.

See also

Next step

Continue with Client to learn to create and manage the WebSocket connection from JavaScript or TypeScript.

Client