WebSocket Transport Guide

Terminology
WebSocket Client/ WebSocket Peer

WebSocket Client or WebSocket Peer represents a remote websocket client application which has successfully established a connection with the WebSocketListener of the UltraESB. This client could be a web browser or any other custom application.

Client ID/ Peer ID

Client ID or Peer ID (WebSocketPeer ID) represents a unique String which is used to identify the websocket client. When Websocket peer authentication is enabled, peer ID is obtained from the AuthenticatedPeer implementation and when the authentication is disabled, peer ID would be the amalgamation of remote client’s IP address and TCP port number. (E.g: 128.345.234.212:23456)

WebSocketMessage

WebSocketMessage is the internal message format used by the UltraESB while performing websocket related communication. WebSocketMessage has a close resemblance to the websocket frame defined in the RFC 6455

Opcode

Opcode denotes the type of WebSocketMessage. Mainly there are 5 types and those are described in detail in Message Types section.

Subscriber Path
Subscriber path of a websocket client is as same as the resource path of the URI, the client has connected to. E.g: Assume a client has connected to 'ws://ws.adroitlogic.com/echotest' URI. Then the subscriber path of that client would be 'echotest'. By default, the ID of a proxy service will be taken as a subscriber path. As an example, if there is a proxy service with ID 'echotest' exposed on WebSocket transport listener, websocket clients will be able to connect to 'echotest' subscriber path and send/receive messages to/from UltraESB.
Optionally you could specify a custom subscriber path in-addition to the proxy service ID using the *ultra.transport.ws.subscribedPath* property name (see sample 452 configuration). But keep in mind that any websocket client connected to the subscriber path specified via that property value will be muted. i.e. any TEXT or BINARY messages from those clients will be ignored. But UltraESB will be able to sent any type of message to those clients. This scenario is very useful when broadcasting messages
Logical Group

Logical group is a concept which could be used to categorize websocket clients connected to the same subscriber path into different groups. It should be noted that the logical group of a websocket peer is obtained from the AuthenticatedPeer implementation when the WebSocket peer authentication is enabled. Further, if the peer authentication is disabled, the logical group of a websocket peer would be null.

Out-going message queue

Each and every websocket peer connected to the UltraESB has an out-going message queue and all the messages transmitted from the UltraESB to the websocket peer is first put into this queue and later dequeued and transmitted.

Overload Policy

When the out-going message queue of a websocket client has reached its maximum limit, overload policy specifies what action should be taken by the UltraESB. During the Initial HTTP handshake request, a websocket client could specify this overload policy using ’*overload-policy:disconnect’*  HTTP header or using the query parameter ’*overload-policy=disconnect’.*

The value disconnect means that when the maximum limit of the out-going queue has reached, the particular websocket peer will be disconnected (i.e. The connection withe the client will be terminated by the UltraESB after sending a WebSocketMessage with opcode CLOSING and the close code 4001.) The default value of the overload policy is ignore which means that when the queue is full all the new incoming messages will be ignored until the queue size falls below the resumeOutMessageQueueAt limit.

WebSocketListener

The WebSocketListener is the WebSocket transport listener implemented on top of the Apache HttpComponents - HttpCore NIO project. The implementation adheres to the RFC 6455 definition of the WebSocket protocol and does not support previous versions. It uses Java Non-Blocking IO (NIO) to process thousands of concurrent connections efficiently by breaking away from the thread-per-socket model. Hence, a very few dedicated IO threads will read/write messages from thousands of concurrent users, and delegate to a worker thread pool for actual message processing through the UltraESB. This way the worker threads are guaranteed not to block on IO. It is a must to pass a reference of the WebSocketContext to the WebSocketListener constructor.

WebSocketSender

The WebSocketSender is the logical WebSocket response sender of the UltraESB. But since the WebSocket protocol is a bi-directional protocol, WebSocketSender depends on the WebSocketListener and it is a must to pass the same WebSocketContext reference you’ve passed onto the WebSocketListener as a constructor argument to the WebSocketSender too.

WebSocketContext

WebSocketContext contains all the parameters of the WebSocket transport implementation. It is a must to declare a spring bean which has the class org.adroitlogic.ultraesb.transport.websocket.WebSocketContext and pass it as a constructer argument to the WebSocketListner and WebSocketSender. Below table contains all the parameters of the WebSocketContext with their definitions and default values.

Parameter name Description Default value

listenerPort

Listening port of the WebSocketListener.

8887

socketTimeout

Socket timeout indicates the time out period of a WebSocketPeer in milliseconds.

120000

listenerShutDownGracePeriod

Within this period all the active connections will be closed and the WebSocketListener will be shutdown

1000

stopNewConnectionsAt

Number of maximum  WebSocketPeers allowed to establish a connection with the WebSocketListener

4096

resumeNewConnectionsAt

When the number of connected WebSocketPeers stopNewConnectionsAt limit, no new WebSocketPeers will be accepted by the WebSocketListener until the number of active WebSocketPeers falls below this limit

3072

limitOutMessageQueueAt

Upper bound for the out-going message queue’s size of a WebSocketPeer. If the queue size exceeds this limit overload policy will be enforced.

1000

resumeOutMessageQueueAt

resumes adding messages to out-going message queue of a WebSocketPeer when the queue size is below this limit

750

maximumPayloadLength

 Maximum Size of the payload permitted for a WebSocketMessage in bytes

1048576

peerAuthentication

Implementation of the WebSocketPeerAuthentication interface

null

Message Types

For every sequence in a proxy service, msg and mediation objects are passed and the current payload of msg object which is related to websocket communication is an instance of org.adroitlogic.ultraesb.api.format.WebSocketMessage class. You can obtain the current payload b invoking msg.getCurrentPayload() method.

The org.adroitlogic.ultraesb.api.format.WebSocketMessage can be divided into several types based on its opcode.

The opcode of a WebSocketMessage has a simillar meaning to the opcode defined in RFC 6455

For a WebSocketMessage you can obtain its opcode by invoking mediation.getWebSocketSupport().getOpcode(msg) method. Messages with below opcodes are passed into the sequences of proxy services by the websocket implementation

TEXT

The payload of the WebSocketMessage  is a String and  you can obtain it by invoking mediation.readFullPayloadAsString(msg) method.

BINARY

The payload of the WebSocketMessage contains binary data and you can obtain the java.nio.ByteBuffer object which contains that binary data as shown below.

final WebSocketMessage currentPayload = (WebSocketMessage) msg.getCurrentPayload();
currentPayload.getPayloadAsByteBuffer();
CLOSING

Messages with this opcode means that a particular websocket client  has been disconnected and you can obtain that client’s ID by invoking mediation.getWebSocketSupport().getOriginPeerID(msg) method. The payload of this message will contain the close code and might contain the close reason.

TIMEOUT

Messages with this opcode means that a session timeout has occurred for a websocket client and the connection has been terminated. You can obtain the timed-out client’s ID by invoking  mediation.getWebSocketSupport().getOriginPeerID(msg) method. The payload of this message type will be empty.

CONNECTED

Messages with this opcode means that a new websocket client has successfully established a connection and you can obtain that client’s ID by invoking mediation.getWebSocketSupport().getOriginPeerID(msg) method. The payload of this message type will be empty.

Note
TIMEOUT and CONNECTED opcodes are only to be used within the UltraESB only and DO NOT send messages with those opcodes to remote websocket clients.
Authentication

The WebSocket protocol defined in RFC 6455 doesn’t specify any particular way to authenticate remote clients. But within the websocket transport implementation of UltraESB, you can authenticate clients by implementing two interface

  1. WebSocketPeerAuthentication

  2. AuthenticatedPeer

Enabling WebSocketPeer authentication
In-order to enable websocket peer authentication pass the spring bean of the WebSocketPeerAuthentication implementation to the WebSocketContext as the value under the property name peerAuthentication
WebSocketPeerAuthentication

An implementation of WebSocketPeerAuthentication interface must define the authenticate() method. The handshakeProperties object passed into this method is a java.util.Properties object which contain all the HTTP header, values and all the query parameters and values which was present in the WebSocket client’s HTTP handshak request.

The implementation can extract any property which is present in the HTTP handshake request and perform client’s authentication accordingly. If the authentication is successful authenticate() method must return an instance of AuthenticatedPeer implementation and return null otherwise.

AuthenticatedPeer

An implementation of AuthenticatedPeer  must define getPeerID(), and getPeerLogicalGroup() methods. getPeerID()  method must return an unique String which could be used to identify the websocket client. getPeerLogicalGroup() must return and array of String which contains the logical groups where the websocket client belongs to. As an example based on the authentication result, websocket clients can be categorized into two groups such as admin, non-admin.

Logical Group
Logical groups enable the application developer to categorize websocket clients into several virtual groups and one websocket client might belong to many groups. The available logical groups must be specified within a proxy service using the property name ultra.transport.ws.groups (see sample 454 configuration)

There are two sample implementations available for the WebSocketPeerAuthentication interface which is used in websocket samples and those are ChatAuthenticator.java and FriendFinderAuthentication.java

Addressing Scheme

WebSocket transport supports sending messages to websocket clients in three different ways

  1. Broadcasting messages

    When broadcasting a message to websocket clients, you can do it in two ways. Either broadcast the message to a subscriber path(s) or broadcast it to a logical group(s).

    1. Broadcasting to subscriber paths

      If you know the subscriber paths you want to broadcast the message initially, you can specify those paths directly as in the sample 452 configuration. Notice the address of the "ws-endpoint" in that configuration is ws://broadcast/subscribers?subscribe_path=stockprice. That means any messages submitted to that endpoint will be broadcasted to all the websocket clients connected to ’stockprice’ subscriber path.

      Note
      The websocket transport implementation ignores the hierarchical structure of subscriber paths. Consider below URIs

      1. ws://ws.adroitlogic.com/sports

      2. ws://ws.adroitlogic.com/sports/cricket

      3. ws://ws.adroitlogic.com/sports/football

        For the clients connected to the three URIs have 'sports', 'sports/cricket', 'sports/football' subscriber paths respectively. It should be noted that if you want to broadcast a message to all the clients connected to the 3 URIs you have to specify the subscriber paths individually. i.e. the destination address of the message should be 'ws://broadcast/subscribers?subscribe_path=sports&subscribe_path=sports/cricket&subscribe_path=sports/football'. Specifying the destination address as 'ws://broadcast/subscribers?subscribe_path=sports' will only broadcast it to the clients connected to the 1st URI.

      If you don’t know the subscriber paths you want to broadcast the message in advance, you can use the ’prefix’ address type. Assume a scenario where a websocket client X sends a message and you want to broadcast that message to all the clients connected to the same subscriber path as the client X. In such cases you can do as follows

      Subscriber path broadcast sample configuration

      <u:proxy id="sample-proxy">
          <u:transport id="ws-listener"/>
          <u:target>
              <u:inSequence>
                  <u:java><![CDATA[
                          final String subscribedPath = mediation.getWebSocketSupport().getOriginatedPeerSubscribedPath(msg);
                          mediation.getWebSocketSupport().setRecipientSubscriberPaths(msg, java.util.Arrays.asList(subscribedPath));
                      ]]></u:java>
              </u:inSequence>
              <u:inDestination>
                  <u:address type="prefix">ws://broadcast/subscribers</u:address>
              </u:inDestination>
          </u:target>
      </u:proxy>

      Notice that since there is no subscriber path given specifically, WebSocket transport will listen to clients connecting at ’sample-proxy’ (the ID of the proxy service) subscriber path. Then within the inDestination, the address type is specified as prefix and hence you only need to specify the prefix of the address. In this case it is ’ws://broadcast/subscribers’ which means that the message should be broadcasted to one or more subscriber path.
      Now we need to specify the suffix of the address and we have done it in the line number 7. For more information about specifying the suffix of address read the Java documentation of the class org.adroitlogic.ultraesb.api.mediation.WebSocketSupport

    2. Broadcasting to logical groups

      Broadcasting messages to logical groups is almost as same as broadcasting messages to Subscriber paths. But in-order to do so, two preconditions must be satisfied and those are
      i. WebSocketPeer authentication should be enabled
      ii. Names of the logical groups should be specified in-advance within the proxy service.
      You have to specify the available logical groups within a proxy service using the property name ultra.transport.ws.groups (see sample 454 configuration)

      If you know the logical groups you want to broadcast the message in-advance you can specify those groups directly as ’ws://broadcast/groups?group_name=foo&group_name=bar’

      If you don’t know the broadcasting groups in-advance, use the ’prefix’ address type and mediation.getWebSocketSupport().setRecipientLogicalGroups(). (see sample 454 configuration)

  2. Sending messages to individual peers

    You can send messages to individual websocket peers. Then again, if you know the IDs of the recipient peers in-advance you can specify the address directly as ’ws://individualpeers?peer_id=foo&peer_id=bar’. And if you don’t know the message recipient peer IDs in-advance, you could use the ’prefix’ address type and mediation.getWebSocketSupport().setRecipientPeers() method. (see sample 453 configuration)

  3. Send a response message

    In-order to send a response message, all you have to do is specify ’response’ as message type. (see sample 450 configuration)

In this topic
In this topic
Contact Us