Skip to content

WebSocket

The WebSocket protocol provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as “packets”, without breaking the connection and the need of additional HTTP-requests.

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. The WebSocket protocol was standardized by the IETF as RFC 6455 in 2011. The current API specification allowing web applications to use this protocol is known as WebSockets.

WebSocket is especially great for services, that require continuous data exchange like:

  • Real-time web applications
  • Chat applications
  • Gaming application

Websocket Vs REST API

SAP BTP, Cloud Foundry Runtime

Cloud Foundry uses a mechanism called SSL/TLS-Termination. If you haven’t heard of this term before, don’t worry, you did now. It basically provides a proxy for SSL connection which handles the cryptographic processing and forwards the unencrypted data to the corresponding service.

How to use WebSockets in the Cloud Foundry Environment

Application Router @sap/approuter

The AppRouter is able to work as an WSS proxy, supporting the whole SAML flow and delegating web socket upgrade requests to internet and on-premise systems behind destinations. For more information, see Configure Application Router.

There are two possible scenarios, see Managed Approuter vs Standalone Approuter.

The Managed Approuter offers Serverless apps together with the HTML5 Application Repository, see Serverless SAP Fiori Apps on SAP BTP, Cloud Foundry environment.

Managed Approuter (BAS default)

Using a managed approuter (like in the SAP Build Work Zone), the configuration has to be part of the HTML5 application.

uml diagram

Standalone Approuter

Using a standalone approuter, the configuration will be part of the approuter and can also be used without the HTML5 runtime using an absolute endpoint.

uml diagram

xs-app.json

The following approuter configuration needs to be used on approuter or HTML5 app level, depending on used scenario (managed vs standalone):

json
{
    ...
    "websockets": {
        "enabled": true
    },
    "routes": [        
        {
            "source": "^/wss/(.*)$",
            "target": "$1",
            "destination": "web-socket-service",
            "csrfProtection": false
            //["authenticationType": ...]
            //["scope": ...]
        }
        ...
    ]
}

Feature History

VersionFeature
11.3.0Support websocket in service2approuter flow
6.5.1Adding sec-websocket-protocol header as the protocol of websockets
6.5.0Adding destination token middleware for websockets
5.14.0Websockets support for HTML5 Application Repository

HTML5 Application Repository

HTML5 application repository enables central storage of HTML5 applications' static content on the SAP BTP, Cloud Foundry environment.

HTML5 application repository allows application developers to manage the lifecycle of their HTML5 applications. In runtime, the repository enables the consuming application, typically the application router, to access HTML5 application static content in a secure and efficient manner.

Destination

Using the Destination service, you map your web-socket-service destination alias to an internet or on-premise system.

The following properties are relevant:

PropertyValueDescription
Name:web-socket-servicedefined in Approuter xs-app.json as destination
Type:HTTP
Description:My WSS backend
URL:http://my.wss.domain.de:443
Proxy Type:Internet
OnPremise
OnPremise uses SAP Cloud Connector and SAP Connectivity Service
Authentication:PrincipalPropagation
BasicAuthentication
None
BasicAuthentication additionally needs user and password information
HTML5.DynamicDestination:trueIf true, the managed application router allows this destination to be used dynamically on the host or path level
sap-client:100Optional

Connectivity Service

The SAP Connectivity Service lets you establish connectivity between your cloud applications and on-premise systems running in isolated networks.

If you are using an OnPremise destination, you additionally have to configure the Cloud Connector.

Minimum Cloud Connector version 2.12 for websocket support!

WebSocket channel is not supported under a certain version of SCC for the channel SAP Cloud Platform to On-Premise. From Cloud Connector 2.12 the new feature is initiated.

2573250 - WebSocket channel for SCP to On-Premise using SAP Cloud Connector

Cloud To On-Premise Access Control Ressources

PropertyValueDescription
URL Path:/sap/bc/apcor your fully qualified path
Active:
WebSocket Upgrade:shown with 🌐 icon
Access Policy:Path and all sub-paths
Description:ABAP Push Channel Framework (APC)

Cloud To On-Premise Access Control Generator

Use the Cloud Connector Scenario Builder to generate a scenario zip file that can be uploaded to the cloud connector.

Message Protocol

Web Sockets allow to send any kind of text and binary message payload.

SAP Push Channel Protocol (PCP)

The Push Channel Protocol is a message based protocol, which is used in context of ABAP Channels, i.e. ABAP Messaging Channels and ABAP Push Channels. The message structure is very similar to a simple HTTP message (see RFC2616).

In Chrome developer tools, a message shows the following log entries:

http
pcp-action:MESSAGE
pcp-body-type:text
field1:value1
field2:value2

this is the body!

If your have to send PCP messages with node.js, use this helper:

ts
import { format } from 'SAPPushChannelProtocol'
ws.send(format('', { Command: 'Notification' }))
SAPPushChannelProtocol.ts (migrated from sap.ui.core.ws.SapPcpWebSocket)
ts
// SAP Push Channel Protocol (PCP)
interface StringMap { [key: string]: string; }

const deserializeRegexp = /((?:[^:\\]|(?:\\.))+):((?:[^:\\\n]|(?:\\.))*)/
const MESSAGE = "MESSAGE"
const SEPARATOR = "\n\n"

/**
 * Format a SAP Push Channel Protocol (PCP) message.
 */
export function format(message: string | Blob | ArrayBuffer, pcpFields?: StringMap) {
    let messageType = typeof message,
        fields = "";

    fields = serializePcpFields(pcpFields, messageType, MESSAGE);

    return fields + message;
}

/**
 * Extracts the pcp-fields from a header string.
 */
function extractPcpFields(header: string) {
    let fields = header.split("\n"),
        lines = [],
        pcpFields: StringMap = {};

    for (var i = 0; i < fields.length; i++) {
        lines = fields[i].match(deserializeRegexp);
        if (lines && lines.length === 3) {
            pcpFields[unescape(lines[1])] = unescape(lines[2]);
        }
    }

    return pcpFields;
};

/**
 * Serializes pcp-fields into a string.
 */
function serializePcpFields(pcpFields: StringMap, messageType: string | Blob | ArrayBuffer, pcpAction: string) {
    var oSerialized = "",
        sFieldName = "",
        sPcpBodyType = "";

    if (messageType === 'string') {
        sPcpBodyType = 'text';
    } else if (messageType === 'blob' || messageType === 'arraybuffer') {
        sPcpBodyType = 'binary';
    }

    if (pcpFields && typeof pcpFields === 'object') {
        for (sFieldName in pcpFields) {
            if (pcpFields.hasOwnProperty(sFieldName) && sFieldName.indexOf('pcp-') !== 0) {
                oSerialized += escape(sFieldName) + ":" + escape(String(pcpFields[sFieldName])) + "\n";
            }
        }
    }

    return "pcp-action:" + pcpAction + "\npcp-body-type:" + sPcpBodyType + "\n" + oSerialized + "\n";
}

/**
 * Escapes a string.
 */
function escape(unEscaped: string) {
    return unEscaped.replace(/\\/g, '\\\\').replace(/:/g, '\\:').replace(/\n/g, '\\n');
}

/**
 * Unescapes a string.
 */
function unescape(escaped: string) {
    let parts = escaped.split("\u0008"),
        unescaped = "";

    for (var i = 0; i < parts.length; i++) {
        // eslint-disable-next-line no-control-regex -- \x08 is used as separator in socket messages
        parts[i] = parts[i].replace(/\\\\/g, "\u0008").replace(/\\:/g, ':').replace(/\\n/g, '\n').replace(/\u0008/g, "\\");
    }

    unescaped = parts.join("\u0008");

    return unescaped;
}

CloudEvents

CloudEvents are not websocket specific, but can be used as a specification, how to design and organize your communication payload.

Even SAP Event Mesh is a fully managed cloud service that allows applications to communicate through asynchronous cloud events. Explorer all events in the SAP API Business Hub.

What is CloudEvents?

cloudeventsCloudEvents is a specification for describing event data in a common way. CloudEvents seeks to dramatically simplify event declaration and delivery across services, platforms, and beyond!

The JSON Format for CloudEvents defines how events are expressed in JavaScript Object Notation (JSON) Data Interchange Format (RFC8259).

Example event with JSON Object-valued data and a content type declaring JSON-formatted data:

json
{
    "specversion" : "1.0",
    "type" : "com.example.someevent",
    "source" : "/mycontext",
    "subject": null,
    "id" : "C234-1234-1234",
    "time" : "2018-04-05T17:31:00Z",
    "comexampleextension1" : "value",
    "comexampleothervalue" : 5,
    "datacontenttype" : "application/json",
    "data" : {
        "appinfoA" : "abc",
        "appinfoB" : 123,
        "appinfoC" : true
    }
}

Designing payloads for Event-Driven-Systems

CloudEvents can help you with your Payload Design to build your own Event-driven architecture (EDA). Maybe also other approaches like AsyncAPI are relevant for you.

If you want to use SAP Event Mesh together with SAPUI5 and WebSockets, read the blog WebSocket’s in SAPUI5 MTA consuming Event Mesh by Dries Van Vaerenbergh.

Client

Most Javascript WebSocket libraries offer a server and client implementation, see Server section for relevant libraries.

SAPUI5

The class sap.ui.core.ws.SapPcpWebSocket provides the necessary PCP methods as an extension to the JavaScript WebSocketclass for SAP APC, and sap.ui.core.ws.WebSocket for regular WebSocket usage.

WARNING

The SAPUI5 libraries do not support reconnection, so you have to implement your own strategy e.g. exponential backoff, which is typically used. If the server goes down and comes back immediately, all clients will reconnect at the exact same second, which causes spike on the server and in worst case bring it back down. To prevent this spike, the backoff algorithm should have a random part.

SAPUI5 example, supporting WebSocket and SapPcpWebWocket with reconnection strategy:

js
sap.ui.define([
    "sap/ui/core/ws/WebSocket",         // used by npm ws
    "sap/ui/core/ws/SapPcpWebSocket",   // used by SAP APC
], function (WebSocket, SapPcpWebSocket) {
    "use strict";
    return Object.extend("namespace.service.WebSocketService", {
        constructor: function (oComponent) {
            this.iInitialReconnectDelay = 1000;
            this.iCurrentReconnectDelay = this.iInitialReconnectDelay;
            this.iMaxReconnectDelay = 16000;
        },
        connect: function (oComponent) {
            var that = this,
                bLocal = false,                
                bManagedApprouter = true,            
                bUseAPC = false,                
                sModePrefix = (bManagedApprouter) ? "/" : "",
                sDestinationAlias = "wss";

            var oWS = (bUseAPC)
                ? new SapPcpWebSocket(
                    sModePrefix + sDestinationAlias 
                        + "/sap/bc/apc/sap/z_cust_service",
                    SapPcpWebSocket.SUPPORTED_PROTOCOLS.v10)
                : new WebSocket((bLocal) 
                    ? "ws://localhost:8080" 
                    : sModePrefix + sDestinationAlias);

            oWS.attachOpen(function (oControlEvent) {
                that.iCurrentReconnectDelay = that.iInitialReconnectDelay;
            });
            oWS.attachError(function (oControlEvent) {});
            oWS.attachClose(function (oControlEvent) {
                oWS = null;
                setTimeout(() => {
                    that.reconnect();
                }, that.iCurrentReconnectDelay + Math.floor(Math.random() * 3000));
            });
            oWS.attachMessage(function (oControlEvent) {
                var oEvent = JSON.parse(
                    oControlEvent.getParameter("data")
                );
                // ...
            });
        },
        reconnect: function () {
            if (this.iCurrentReconnectDelay < this.iMaxReconnectDelay) {
                this.iCurrentReconnectDelay *= 2;
            }
            this.connect();
        }
    });
});

PCP Libraries

SAP provides with sap-pcp-websocket.js the necessary PCP methods. This library is available in the mime repository (under the repository path /sap/public/bc/ur) and in ABAP Application Server with 740 SP10. For accessing the library refer to note 2050139.

Server

There are multiple Node.js based WebSocket libraries available like ws, Socket.IO or SockJS. For a more detailed overview, see Top WebSocket libraries for Node.js in 2022.

SAP NetWeaver AS ABAP starting with version 7.40 (SP5) is an option to offer a WebSocket server using the ABAP Push Channel (APC).

ABAP Channels

ABAP Channels technology targets to replace inefficient eventing based on polling techniques through push notifications based on publish-subscribe infrastructure and WebSockets in ABAP.

The ABAP Channels support the following use cases:

Use CaseDescription
ABAP Push Channel (APC)The WebSocket integration in ABAP
ABAP Messaging Channel (AMC)Eventing framework for message exchange between different ABAP sessions based on publish/subscribe channels
Collaboration scenarioUsingAPC and AMC to push messages from ABAP sessions to WebSocket clients by binding publish/subscribe channels to a WebSocket connection

Useful Resources

If you struggle with problems, the FAQ – ABAP Channels will give answers to the most asked questions or read:

General recommendations for usage of ABAP Channels

  1. Use one WebSocket connection for collaboration scenario
  2. Use SAP Push Channel Protocol (PCP) messaging protocol
  3. Use the secure variant of WebSocket, i.e. wss instead of ws

Internet Communication Framework (ICF)

If you are using an on-premise destination with BasicAuthentication strategy, you maybe have to change the logon procedure of your custom SICF node:

  • Use transaction SICF to edit your custom service.
  • Inside section Logon Data change Procedure to "Alternative Logon Procedure".
  • In the Logon Procedure List make sure, "Basic Authentication" will be the first entry in the list.

Cross-origin resource sharing (CORS)

For cross-origin-access to an APC application the APC framework also supports some aspects of The Web Origin Concept (RFC 6454). In order to allow access to an APC application from an external server the white list maintenance via transaction SAPC_CROSS_ORIGIN has to be done accordingly.

Whitelist issues

Use transaction SM21 to monitor, if your websocket upgrade request is blocked, because you have CORS issues.

Add your Approuter host and port to the whitelist

The Approuter host needs to be added to the whitelist, because it forwards the SAP BTP Cloud websocket uri through the cloud connector to the on-premise AS ABAP system running on a different host.

Relevant SAP Notes

  • 2353453 - Check of ABAP Push Channel (APC) WebSocket connection
  • 2123214 - Error handling for ABAP Channels using SYSLOG entries
  • 2050139 - SAP Push Channel Protocol (PCP) JavaScript library for ABAP Push Channel (APC)
  • 2007212 - Tuning SAP Web Dispatcher and ICM for high load
  • 2349642 - Configuration for high load on inbound WebSocket connections for ABAP Push Channel applications

Feature History

ReleaseFeature
ABAP Platform 2020
(SAP_BASIS 7.55)
Support for AMQP 1.0.0 (AMQP client only).
ABAP Platform 1909
(SAP_BASIS 7.54)
WebSocket RFC, i.e., SAP Remote Function Call (RFC) via WebSocket. (Although WebSocket RFC is not an ABAP Channels feature, it is listed here, as it enhances the WebSocket protocol support of the ABAP Platform.)
ABAP Platform 1809
(SAP_BASIS 7.53)
Support for MQTT v3.1.1 (MQTT client only).
Automatic start-up configuration for the ABAP Daemons.
NetWeaver AS ABAP 7.52First version of the ABAP Daemons.
NetWeaver AS ABAP 7.51Support for large messages (see questions regarding message size limits). Additional functionalities for the WebSocket PCP subprotocol, e.g., connection health checks and APC Multiplexing.
NetWeaver AS ABAP 7.50Support for APC TCP Socket.
Also, the application server can open APC client connections and supports a stateful ABAP session handling model for APC connections.
NetWeaver AS ABAP 7.40
SP5/SP8
The first version of the ABAP Channels infrastructure has been released with AS ABAP 7.40 SP5. This includes AMC and APC WebSocket. We highly recommend to use at least release 7.40 SP8, as this is the prerequisite for the recommended Push Channel Protocol (PCP).

WebSocket RFC

By using WebSocket as an alternative transport layer for Remote Function Call (RFC), WebSocket RFC, the new web-enabled flavor of RFC, combines the benefits of a standardized transport layer with the well-known advantages of RFC and the benefits of FAST RFC Serialization. WebSocket RFC is easy to administrate and is secure by default.

Profit from a New Standardized Transport Layer for RFC

  • You no longer need a VPN tunnel for RFC connections across network borders, avoid the time-consuming process of setting up a VPN tunnel and the costs entailed by this in terms of time and money required for the respective certificates.
  • Hybrid scenarios are possible without an intermediate SAP Cloud Connector (in this weblog further on, for reasons of simplicity named “Cloud Connector” ).
  • You can use standard HTTPS ports (443) – the normal HTTP port (80) should not be used because TLS is mandatory for WebSocket RFC as described below -, standard HTTP network infrastructure and tools since WebSocket uses an upgraded HTTPS connection.

In a nutshell, you avoid the disadvantages of the proprietary CPIC protocol so far underlying all RFC communication, and, instead, benefit from a standardized, web-enabled transport layer.

ws: a Node.js WebSocket library ws

The ws package is a simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.

For a comlete CAP based end-2-end example, have a look at the CAP WebSockets Demo.

The following snippet showcases the usage of multiple channels by leveraging cloud events and storing user infos in server.clients {Set}:

js
import WebSocket, { WebSocketServer } from 'ws';
import { v4 as uuidv4 } from 'uuid';

const wss = new WebSocketServer({ port: process.env.PORT || 8080 });

wss.on('connection', function connection(ws, req, client) {
    const params = new URLSearchParams(req.url.substring(1));
    
    // store user info inside client
    ws.id = req.headers['sec-websocket-key'];    
    ws.user = {
        "id": ws.id,
        "userAgent": params.get('userAgent'),
        "loginName": params.get('loginName'),
        "firstName": params.get('firstName'),
        "lastName": params.get('lastName')
    }

    // send logon info to current client
    ws.send(apiUserInfoEvent(ws.user, wss.clients));

    // broadcast logon info to all other clients (except self)
    wss.clients.forEach(function each(client) {
        if (client !== ws && client.readyState === WebSocket.OPEN) {
            client.send(apiUserLogonEvent(ws.user));
        }
    });

    ws.on('close', function close(reasonCode, description) {
        // broadcast logoff info to all other clients (except self)
        wss.clients.forEach(function each(client) {
            if (client !== ws && client.readyState === WebSocket.OPEN) {
                client.send(apiUserLogoffEvent(ws.user));
            }
        });
    });

    ws.on('message', function message(data, isBinary) {
        const event = JSON.parse(data)
        switch (event.type) {
            case 'ns.event.broadcast.message':
            case 'ns.event.broadcast.toast':
                // broadcast to all clients
                wss.clients.forEach(function each(client) {
                    if (client.readyState === WebSocket.OPEN) {
                        client.send(JSON.stringify(event))
                    }
                });
                break;
        }
    });

    // Cloud Events spec
    // https://github.com/cloudevents/spec
    const apiUserInfoEvent = function (user, clients) {
        var users = [];
        clients.forEach(function each(client) {
            users.push(client.user);
        })
        return JSON.stringify({
            "specversion": "1.0",
            "type": "namespace.event.user.info",
            "source": "/me",
            "subject": null,
            "id": uuidv4(),
            "time": new Date().toISOString(),
            "datacontenttype": "application/json",
            "data": {
                "user": user,
                "users": users
            }
        })
    }
});

Examples

ExampleDescriptionComponentsAuthor
CAP WebSockets DemoCAP based end-2-end WebSocket demo. Showcasing JWT authenticated WebSocket behind Approuter used in SAPUI5 Fiori Shell Plugin.APPROUTER CAP WS SAPUI5 MTAGregor Wolf
SAPUI5 MTA consuming SAP Event MeshWebSocket’s in SAP UI5 Multi Target Applications consuming Enterprise Messaging?APPROUTER WS EVENTMESHSAPUI5 MTADries Van Vaerenbergh
xsa-node-websocketsSAP HANA XS Advanced - Web Sockets within Node.js SAP HANA applicationsWS XSA SAPUI5Thomas Jung
ABAP Push Channel SAPUI5 DemoSAPUI5 Demo application using the ABAP Push Channel (APC, WebSockets) and ABAP Messaging Channel (AMC)WS APC AMC SAPUI5Gregor Wolf