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.
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:
my-project/
└── websocket/
└── index.gox The file uses the module websocket:
module websocket The input function should be called Index and respect this signature:
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.
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:
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:
| API | Documentation |
|---|---|
When() | Lifecycle |
On() and Emit() | Events |
Emit(...).Await() | Requests |
Join(), Leave(), LeaveAll(), Groups() and EmitTo() | Groups |
Fields
| Field | Description |
|---|---|
| Session | It provides access to the associated session. |
| Handshake | Keep the data from the request that started the connection. |
| Siblings | It 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.
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.
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.
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.
origin := socket.Handshake.Origin()
userAgent := socket.Handshake.UserAgent() It also allows you to check the URL, headers, cookies and query parameters received:
room, exists := socket.Handshake.Query.Get("room") authorization, exists := socket.Handshake.Headers.Get("Authorization") Its methods provide direct access to the main data:
| Method | Description |
|---|---|
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.
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.
connectedDevices := socket.Siblings.Count() All() returns a snapshot of the current sockets:
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:
socket.Siblings.Emit("profileUpdated") Disconnect() close all sockets included in the collection:
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
| Method | Description |
|---|---|
| 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.
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.
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.
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.
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.
socket.When("connect", func(isReconnect bool) {
socket.Emit("welcome")
}) On() records events sent by the client.
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.
socket.Emit("welcome", "Hola, George") You can send one or more values:
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.
socket.Disconnect(gox.WSCloseNormal) GOX automatically uses the description associated with the code.
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:
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
Indexto 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
Siblingsto 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.