WebSocket for Real-time Apps

aah provides a flexible and easier way to implement WebSocket capabilities into the application. aah enables maximum usability and provides WebSocket power to the user.

As always, aah is structured, organized and maintainable. aah shines more with application growth.

  • Enable WebSocket under server section in aah.conf.
  • Add WebSocket’s under package <app-base-dir>/app/websockets.
  • Add WebSocket’s routing definition into routes.conf; it is similar to controllers, easy right!

Table of Contents

Configuration

As you know, one of the notable feature of aah is configuration driven. This configuration goes under section server { ... }.

# --------------------------------------------------------------------------
# WebSocket Configurations
# Doc: https://docs.aahframework.org/websocket.html#configuration
# --------------------------------------------------------------------------
websocket {
  # Default value is `false`.
  enable = true

  # Origin Check Configuration
  # It is useful when having only browser based clients. It adds another layer
  # of security protection.
  origin {
    # When origin check is enabled, aah does check against HTTP Header
    # `Origin` value.
    # Default value is `false`.
    check = false

    # Origin whitelist
    # No default values.
    #whitelist = [
    #  "https://websocket.org",
    #]
  }
}

Creating WebSocket

It is simple, similar to aah’s Controller. GoDoc

// HotNewsWebSocket holds an implementation of News Broadcast.
type HotNewsWebSocket struct {
  *ws.Context
}

// ByTopic method handles topic related clients.
func (w *HotNewsWebSocket) ByTopic(topic string)  {
  for {
    // Implementation goes here
    // Refer to simple chat example to get an idea
    //
  }
}

// and so on ...

Custom ID

aah support custom ID generation for each WebSocket connection. By default aah creates GUID using MongoDB Object ID alogrithm.

// IDGenerator func type used to implement custom WebSocket connection ID.
// By default aah generates GUID using MongoDB Object ID algorithm.
type IDGenerator func(ctx *Context) string

// Setting ID generator via
aah.App().WSEngine().SetIDGenerator(MyCustomIDGenerator)

Adding Route

Similar to aah’s Controller routes definition, it is easy. The flexibility of routes definition is also supported in WebSocket.

Note: aah understands WebSocket route definition mainly by the following two attributes.

  • method = "WS"
  • websocket = "HotNewsWebSocket"
by_topic {
  path = "/hot-news/:topic"
  method = "WS"
  websocket = "HotNewsWebSocket"
  action = "ByTopic"

  # By default, 'anti_csrf_check', 'cors' and 'max_body_size' configs are not
  # applicable for WebSocket; even if provided, it won't affect the WebSocket route.
  #
  # Use `server.websocket.origin { ... }` for enforcing SAME-ORIGIN policy.
}

Sending Header on Connection Upgrade

aah sends ctx.Header values during a successful WebSocket connection upgrade.

Note: Incoming request headers are accessible via ctx.Req.Header.

// Adding header into `ws.Context`
// Use event `ws.OnPreConnect` to add header into context.
func AddHeaderIntoContext(eventName string, ctx *ws.Context)  {
  ctx.Header.Set("My-Header", "Header value")
}

Events

aah provides following events as part of the WebSocket lifecycle. It is published for each WebSocket connection made to the aah server.

As a recommended approach, create an event handler in appropriate package and register it in the file <app-base-dir>/app/init.go.

EventDescription
OnPreConnectPublished just before connection gets upgraded to WebSocket. It provides a control of accepting incoming request or reject it using ctx.Abort(errorCode)
OnPostConnectPublished just after the successful WebSocket connection is established with the aah server.
OnPostDisconnectPublished just after the WebSocket client got disconnected. It could have occurred due to graceful disconnect, network related error, etc.
OnErrorPublished whenever error occurs in the lifecycle such as Origin Check failed, WebSocket or WebSocket Action not found, WebSocket Action parameter parse error, and WebSocket upgrade fails.

Call method ctx.ErrorReason() to know the reason for the error.

Event Callback Signature

// EventCallbackFunc func type used for all WebSocket event callbacks.
func(eventName string, ctx *ws.Context)

Code Snippet from app/init.go

// WebSocket Events
// Doc: https://docs.aahframework.org/websocket.html#events
//
// Subscribing WebSocket events on app start.
func SubscribeWebSocketEvents(_ *aah.Event) {
  wse := aah.App().WSEngine()

  // Custom ID Generator
  wse.SetIDGenerator(websockets.MyCustomIDGenerator)

  wse.OnPreConnect(websockets.HandleEvents)
  wse.OnPostConnect(websockets.HandleEvents)
  wse.OnPostDisconnect(websockets.HandleEvents)
  wse.OnError(websockets.HandleEvents)
}

// SubscribeWebSocketEvents method gets registered on App Start event to
// subscribe WebSocket events from aah WebSocket Engine.
aah.App().OnStart(SubscribeWebSocketEvents)