Lifecycle

The WebSocket life cycle begins when the client tries to establish the connection and ends when it is closed definitely.

GOX manages the handshake, connection, disconnection and reconnection. The client and server can register handlers to react to these changes.

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

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

socket.When("disconnect", func(details gox.WSDisconnectReason) {
    log.Printf("Conexión cerrada: %d", details.Code)
})

socket.When("error", func(err gox.GoxSocketError) {
    log.Printf("Error WebSocket: %s", err.Message)
})
js
socket.when("connect", isReconnect => {
    console.log("Conectado", { isReconnect })
})

socket.when("disconnect", details => {
    console.log("Desconectado", details)
})

socket.when("error", error => {
    console.error(error)
})

Both APIs describe the same communication, although each environment receives the information corresponding to its side of the connection.

See Socket to know the API of the server used during these stages. To learn how to create and configure the connection from JavaScript or TypeScript, see Client.


Events

EventServerClientDescription
handshakeYesNo.Authorizes or rejects the connection before the upgrade.
connectYesYesIt indicates that the connection is available.
disconnectYesYesDescribe the connection closure.
errorYesYesIt reports an operating or protocol error.

handshake only exists on the server because it uses the HTTP context that started the connection.

Other events can be observed from both sides by When() in Go and when() in JavaScript.


Handshake

Handshake is the first stage of the connection.

The server can record the event handshake to authorize or reject the client before completing the upgrade to WebSocket:

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

The handler receives the original HTTP context and must return a boolean value:

  • true Accept the connection.
  • false He rejects the connection.

If the event is not recorded, GOX automatically applies its default validation of the same origin.

The context makes it possible to consult information necessary to make the decision:

gox
socket.When("handshake", func(ctx *gox.Context) bool {
    return ctx.Session.Has("userID")
})

During this stage, the socket has not yet been registered and socket.ID() return an empty string.

If the handshake is rejected, the server responds with 403 Forbidden and does not run connectThe client first gets a mistake. HANDSHAKE_FAILED and then the event disconnectThe connection remains closed and GOX does not try to reconnect it automatically.

js
socket.when("error", error => {
    if (error.code === "HANDSHAKE_FAILED") {
        console.log("El handshake fue rechazado o no pudo completarse")
    }
})

The browser does not allow for a certain distinction between deliberate rejection and other problems that prevent the handshake from being completed.

Do not keep the context received after completion of the handler. To subsequently access the initial request data, use socket.Handshake.


Connect

The event connect is run when the connection is available.

Servidor:

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

Cliente:

js
socket.when("connect", isReconnect => {
    console.log("Conectado", { isReconnect })
})

The argument isReconnect indicates how the connection was established:

ValueDescription
falseIt's the client's initial connection.
trueThe connection was established by a reconnection.

From connect, the server can already use socket.ID(), issue events, consult other sockets and work with groups or channels.

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

The client can use isReconnect to avoid repeating operations that only correspond to the initial connection:

js
socket.when("connect", isReconnect => {
    if (!isReconnect) {
        initializeApplication()
    }
})

Recording connect replace the previous handler of that event.


Disconnect

The event disconnect It runs when the connection ends.

Servidor:

gox
socket.When("disconnect", func(details gox.WSDisconnectReason) {
    log.Printf("Socket cerrado: %d", details.Code)
})

Cliente:

js
socket.when("disconnect", details => {
    console.log(details.code)
    console.log(details.reason)
    console.log(details.willReconnect)
})

The information received describes the cause of the closure:

GoJavaScriptDescription
CodecodeCode of closure.
ReasonreasonDescription associated with the code.
CleancleanIt indicates whether the transport correctly completed the closure.
InitiatorinitiatorIt indicates who started or caused the closure.
WillReconnectwillReconnectIt indicates whether the client will try to reconnect.

initiator may contain:

ValueDescription
"client"The client requested the disconnection.
"server"The server closed the connection.
"unknown"It was not possible to determine the initiator, usually due to an interruption of transport.

The server can close the connection by Disconnect():

gox
socket.Disconnect(gox.WSClosePermanent)

The client can close it by disconnect():

js
import { Socket, WSCloseCode } from "@gox/websocket"
const socket = new Socket()
socket.disconnect(WSCloseCode.NORMAL)

A direct call to socket.disconnect() disable the automatic reconnection for that socket.


Reconnection

The automatic reconnection is default-active in the client.

GOX tries to recover the connection when the closing code represents a recoverable interruption. The wait gradually increases between attempts to reach the set limit.

js
const socket = new Socket({
    reconnect: {
        enabled: true,
        delay: 1000,
        maxDelay: 30000,
        attempts: 5
    }
})
OptionDefault valueDescription
enabledtrueIt activates the automatic reconnection.
delay1000Initial wait in milliseconds.
maxDelay30000Wait a minute between attempts.
attempts0Maximum number of attempts. 0 It doesn't set a limit.

It can also be completely deactivated:

js
const socket = new Socket({
    reconnect: false
})

When a reconnection occurs:

  • The client retains the same instance of Socket.
  • The handlers registered by when() and on() are preserved.
  • The existing channels try to join again.
  • The server creates a new instance of gox.Socket.
  • The new connection receives another identifier.
  • The session can be held if it remains valid.
  • connect receives isReconnect with the value true.

The previous connection is over. For this reason, the new socket of the server does not retain its groups or the state stored only in the previous instance.

To keep information between connections, use the session or other persistent storage. Sessions to know their storage, persistence and life cycle.


Close codes

GOX provides shared codes between Go and JavaScript.

GoJavaScriptCodeReconecta
WSCloseNormalWSCloseCode.NORMAL1000No.
WSCloseGoingAwayWSCloseCode.GOING_AWAY1001Yes
WSCloseProtocolWSCloseCode.PROTOCOL1002No.
WSCloseUnsupportedWSCloseCode.UNSUPPORTED1003No.
WSCloseAbnormalWSCloseCode.ABNORMAL1006Yes
WSCloseInvalidDataWSCloseCode.INVALID_DATA1007No.
WSClosePolicyWSCloseCode.POLICY1008No.
WSCloseMessageLargeWSCloseCode.MESSAGE_LARGE1009No.
WSCloseInternalWSCloseCode.INTERNAL1011Yes
WSCloseRestartWSCloseCode.RESTART1012Yes
WSCloseTryLaterWSCloseCode.TRY_LATER1013Yes
WSClosePermanentWSCloseCode.PERMANENT4000No.
WSCloseReconnectWSCloseCode.RECONNECT4001Yes
WSCloseSessionExpiredWSCloseCode.SESSION_EXPIRED4002No.
WSCloseSessionDestroyedWSCloseCode.SESSION_DESTROYED4003No.

WSCloseDefault and WSCloseCode.DEFAULT They are equivalent to normal closure.

Use WSCloseReconnect when the server needs to close the current connection and ask the client to establish a new one:

gox
socket.Disconnect(gox.WSCloseReconnect)

Use WSClosePermanent when the connection must not be automatically recovered:

gox
socket.Disconnect(gox.WSClosePermanent)

The codes reserved for sessions are normally administered by GOX when a session expires or is destroyed.

The reconnection also depends on the client's configuration and the number of attempts available. A recoverable code does not guarantee a reconnection if it was disabled or reached its limit.


Error

The event error reports operational problems without assuming that the connection is over.

Servidor:

gox
socket.When("error", func(err gox.GoxSocketError) {
    log.Printf("%s: %s", err.Code, err.Message)
})

Cliente:

js
socket.when("error", error => {
    console.error({
        code: error.code,
        phase: error.phase,
        event: error.event,
        retryable: error.retryable,
        message: error.message
    })
})

An error contains the following main information:

GoJavaScriptDescription
CodecodeStable error identifier.
MessagemessageDescription of the problem.
PhasephaseStage at which it happened.
EventeventRelated event, when appropriate.
RetryableretryableIt indicates whether the operation can be recovered.

The main phases are:

PhaseDescription
handshakeAuthorization or initial establishment.
connectionCreation or registration of the connection.
transportReading, writing or interruption of transport.
handlerRunning a handler.
protocolCodification, packages or protocol status.
bindingLinking arguments to the client.
sessionPersistence, activity or expiry of the session.

The event error and the event disconnect perform different functions:

  • error describes a problem.
  • disconnect confirms the connection's over.

An error can occur without closing the socket. disconnect or state to determine the status of the connection.


Sequence

An accepted initial connection follows this order:

  1. The client creates a Socket.
  2. GOX starts the HTTP handshake.
  3. The server runs Index.
  4. The server runs handshake, if it was registered.
  5. GOX accepts and records the connection.
  6. The server runs connect.
  7. The client runs connect.
  8. The client and the server exchange events.
  9. The connection is over.
  10. The client and the server run disconnect.

If the closure allows to reconnect, the client expects and starts a new attempt. The server creates another socket and events connect receive isReconnect with the value true.


Best practices

  • Search the life cycle handlers before starting to exchange events.
  • Use handshake to authorize the connection before the upgrade.
  • Do not use socket.ID() during Index or handshake.
  • Check. isReconnect before repeating exclusive tasks of the initial connection.
  • Store the recoverable status in the session or in persistent storage.
  • Always handle the event error.
  • Use disconnect to confirm that the connection is over.
  • Select a closing code that correctly represents the cause.
  • Use WSCloseReconnect only when the client has to recover the connection.
  • Use WSClosePermanent where there should be no automatic reconnection.
  • Do not keep the HTTP context received by handshake.

See also

Next step

Continue with Events to learn how to exchange information between the client and the server.

Events