Specification: Ballerina WebSocket Library

Owners: @shafreenAnfar @bhashinee
Reviewers: @shafreenAnfar
Created: 2021/12/09
Updated: 2022/05/05
Edition: Swan Lake

Introduction

This is the specification for the WebSocket standard library of Ballerina language, which provides WebSocket client-server functionalities.

The WebSocket library specification has evolved and may continue to evolve in the future. The released versions of the specification can be found under the relevant GitHub tag.

If you have any feedback or suggestions about the library, start a discussion via a GitHub issue or in the Slack channel. Based on the outcome of the discussion, the specification and implementation can be updated. Community feedback is always welcome. Any accepted proposal, which affects the specification is stored under /docs/proposals. Proposals under discussion can be found with the label type/proposal in GitHub.

The conforming implementation of the specification is released and included in the distribution. Any deviation from the specification is considered a bug.

Contents

  1. Overview
  2. Listener
  3. Service Types
  4. Client
  5. Securing the WebSocket Connections
  6. Samples

1. Overview

WebSocket is a protocol that allows a long held full-duplex connection between a server and client. This specification elaborates on how Ballerina language provides a tested WebSocket client and server implementation that is compliant with the RFC 6455.

2. Listener

The WebSocket listener can be constructed with a port or an http:Listener. When initiating the listener it opens up the port and attaches the upgrade service at the given service path. It is also worth noting that upgrade service is quite similar to an HTTP service.

2.1. Listener Configurations

When initializing the listener, following configurations can be provided,

ListenerHttp1Settings record contains the settings related to HTTP/1.x protocol. This is an included record from the HTTP package, and this will only be applicable to the initial WebSocket upgrade request.

The actual ListenerHttp1Settings record in the HTTP package contains the following fields.

ListenerSecureSocket record contains configurations related to enabling SSL/TLS on the listener side, and it is an included record from the HTTP package. More details and examples of how to configure them can be found in a following section on Securing the WebSocket Connections.

The actual ListenerSecureSocket record in the HTTP package contains the following fields.

RequestLimitConfigs record represents configurations related to maximum sizes of URI, headers and entity body which is also an included record from the hTTP package as this will only be applicable to the initial WebSocket upgrade request.

The actual RequestLimitConfigs record in the HTTP package contains the following fields.

2.2. Initialization

The WebSocket listener can be initialized by providing the port or a http:Listener and optionally a ListenerConfiguration.

3. Service Types

3.1. UpgradeService

Upgrade service is pretty much similar to an HTTP service. It has a single get resource, which takes in an http:Request optionally. The get resource returns a websocket:Service to which incoming messages get dispatched after a successful WebSocket connection upgrade. This resource can be used to intercept the initial HTTP upgrade with custom headers or to cancel the WebSocket upgrade by returning an error.

3.1.1. UpgradeService Configurations

When writing the service, following configurations can be provided,

3.2. WebSocket Service

Once the WebSocket upgrade is accepted by the UpgradeService, it returns a websocket:Service. This service has a fixed set of remote functions that do not have any configs. Receiving messages will get dispatched to the relevant remote function. Each remote function is explained below.

3.2.1. Remote methods associated with WebSocket Service

onOpen

As soon as the WebSocket handshake is completed and the connection is established, the onOpen remote method is dispatched.

onTextMessage

The received text messages are dispatched to this remote method.

onBinaryMessage

The received binary messages are dispatched to this remote method.

onMessage

The received messages are dispatched to this remote method. Data binding support is provided to accept the messages as anydata.

onPing and onPong

The received ping and pong messages are dispatched to these remote methods respectively. You do not need to explicitly control these messages as they are handled automatically by the services and clients.

onIdleTimeout

This remote method is dispatched when the idle timeout is reached. The idleTimeout has to be configured either in the WebSocket service or the client configuration.

onClose

This remote method is dispatched when a close frame with a statusCode and a reason is received.

onError

This remote method is dispatched when an error occurs in the WebSocket connection. This will always be preceded by a connection closure with an appropriate close frame.

4. Client

websocket:Client can be used to send and receive data synchronously over WebSocket connection. The underlying implementation is non-blocking.

4.1. Client Configurations

When initializing the client, following configurations can be provided,

4.2. Initialization

A client can be initialized by providing the WebSocket server url and optionally the ClientConfiguration.

4.3. Send and receive messages using the Client

writeTextMessage

writeTextMessage API can be used to send a text message. It takes in the message to be sent as a string and returns an error if an error occurs while sending the text message to the connection.

writeBinaryMessage

writeBinaryMessage API can be used to send a binary message. It takes in the message to be sent as a byte[] and returns an error if an error occurs while sending the binary message to the connection.

writeMessage

writeMessage API can be used to send messages. It takes in the message to be sent as subtypes of anydata and returns an error if an error occurs while sending the message to the connection. The input data is internally converted to relevant frame type as follows,

  • subtypes of string, xml, json - write out using text frames
  • byte[] - write out using binary frames

readTextMessage

readTextMessage API can be used to receive a text message. It returns the complete text message as a string or else an error if an error occurs while reading the messages.

readBinaryMessage

readBinaryMessage API can be used to receive a binary message. It returns the complete binary message as a byte[] or else an error if an error occurs while reading the messages.

readMessage

readMessage API can be used to receive a message without prior knowledge of message type. anydata type is supported by this API. The contextually-expected data type is inferred from the LHS variable type. If there is an error when converting the expected data type or else any other error occurs while reading the messages, the respective error will be returned from the API

close

close API can be used to close the connection. It takes in the optional parameters statusCode for closing the connection, reason for closing the connection if there is any and the timeout to wait until a close frame is received from the remote endpoint.

ping

ping API can be used to send ping messages. It takes in the message to be sent as a byte[] and returns an error if an error occurs while sending the ping message to the connection.

pong

pong API can be used to send pong messages. It takes in the message to be sent as a byte[] and returns an error if an error occurs while sending the pong message to the connection.

onPing and onPong remote methods

To receive ping/pong messages, users have to register a websocket:PingPongService when creating the client. If the service is registered, receiving ping/pong messages will get dispatched to the onPing and onPong remote functions respectively.

If the user has implemented onPing on their service, it's user's responsibility to send the pong frame. It can be done simply by returning the data from the remote function, or else can be done using the pong API of websocket:Caller. If the user hasn't implemented the onPing remote function, pong will be sent automatically.

5. Securing the WebSocket Connections

Ballerina provides inbuilt support for SSL/TLS and configurations to enforce authentication and authorization such as Basic Auth, JWT auth, and OAuth2.

5.1. SSL/TLS

You can configure a secure socket for your WebSocket listener and client to upgrade to a TCP connection with TLS.

The TLS-enabled Listener

The TLS-enabled Client

5.2. Authentication and Authorization

Listener

The Ballerina WebSocket library provides built-in support for the following listener authentication mechanisms that are validated in the initial upgrade request.

  1. Basic authentication
  2. JWT authentication
  3. OAuth2 authentication

To enable one of the above, you should configure the auth field in websocket:ServiceConfig annotation which consists of the following records:

  1. FileUserStoreConfigWithScopes
  2. LdapUserStoreConfigWithScopes
  3. JwtValidatorConfigWithScopes
  4. OAuth2IntrospectionConfigWithScopes

Each of the above records consists of configurations specific to each type as FileUserStoreConfig , LdapUserStoreConfig ,JwtValidatorConfig and OAuth2IntrospectionConfig respectively. You just have to configure them and there will be no need for any extensions or handlers. Ballerina will perform the required validation for you.

Client

The Ballerina WebSocket client can be configured to send authentication information to the endpoint being invoked. The Ballerina WebSocket library also has built-in support for the following client authentication mechanisms.

  1. Basic authentication
  2. JWT authentication
  3. OAuth2 authentication

The following code snippet shows how a WebSocket client can be configured to call a secured endpoint. The auth field of the client configurations (websocket:ClientConfiguration) should have either one of the CredentialsConfig, BearerTokenConfig, JwtIssuerConfig, OAuth2ClientCredentialsGrantConfig, OAuth2PasswordGrantConfig, and OAuth2RefreshTokenGrantConfig records. Once this is configured, Ballerina will take care of the rest of the validation process.

6. Samples

Listener

Client