Appearance
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
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.
Application Router
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.
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.
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
Version | Feature |
---|---|
11.3.0 | Support websocket in service2approuter flow |
6.5.1 | Adding sec-websocket-protocol header as the protocol of websockets |
6.5.0 | Adding destination token middleware for websockets |
5.14.0 | Websockets 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:
Property | Value | Description |
---|---|---|
Name: | web-socket-service | defined 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: | true | If true, the managed application router allows this destination to be used dynamically on the host or path level |
sap-client: | 100 | Optional |
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
Property | Value | Description |
---|---|---|
URL Path: | /sap/bc/apc | or 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?
CloudEvents 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.
socket.io
socket-io enables real-time bidirectional event-based communication.
Once you have installed the Socket.IO client library, you can now init the client. The complete list of options can be found here.
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 Case | Description |
---|---|
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 scenario | UsingAPC 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
- Use one WebSocket connection for collaboration scenario
- Use SAP Push Channel Protocol (PCP) messaging protocol
- 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
Release | Feature |
---|---|
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.52 | First version of the ABAP Daemons. |
NetWeaver AS ABAP 7.51 | Support 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.50 | Support 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.
@cap-js-community/websocket
WebSocket adapter for CDS supporting ws and socket.io.
Exposes a WebSocket protocol via WebSocket standard or Socket.IO for CDS services. Runs in context of the SAP Cloud Application Programming Model (CAP) using @sap/cds (CDS Node.js).
ws: a Node.js WebSocket library
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
}
})
}
});
socket.io
Socket.IO enables real-time bidirectional event-based communication.
It consists of:
- a Node.js server (this repository)
- a Javascript client library for the browser (or a Node.js client)
Feature | Description |
---|---|
Reliability | For this purpose, it relies on Engine.IO, which first establishes a long-polling connection, then tries to upgrade to better transports that are "tested" on the side, like WebSocket. |
Auto-reconnection support | Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. |
Disconnection detection | A heartbeat mechanism is implemented at the Engine.IO level, allowing both the server and the client to know when the other one is not responding anymore. |
Binary support | Any serializable data structures can be emitted. |
Simple and convenient API | socket.emit, io.emit, socket.on... |
Cross-browser | Android, Firefox, Chrome, IE, iPhone, Edge, Safari |
Multiplexing support | In order to create separation of concerns within your application (for example per module, or based on permissions), Socket.IO allows you to create several Namespaces , which will act as separate communication channels but will share the same underlying connection. |
Room support | Within each Namespace , you can define arbitrary channels, called Rooms , that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it. |
Examples
Example | Description | Components | Author |
---|---|---|---|
CAP WebSockets Demo | CAP based end-2-end WebSocket demo. Showcasing JWT authenticated WebSocket behind Approuter used in SAPUI5 Fiori Shell Plugin. | APPROUTER CAP WS SAPUI5 MTA | Gregor Wolf |
SAPUI5 MTA consuming SAP Event Mesh | WebSocket’s in SAP UI5 Multi Target Applications consuming Enterprise Messaging? | APPROUTER WS EVENTMESH SAPUI5 MTA | Dries Van Vaerenbergh |
xsa-node-websockets | SAP HANA XS Advanced - Web Sockets within Node.js SAP HANA applications | WS XSA SAPUI5 | Thomas Jung |
ABAP Push Channel SAPUI5 Demo | SAPUI5 Demo application using the ABAP Push Channel (APC, WebSockets) and ABAP Messaging Channel (AMC) | WS APC AMC SAPUI5 | Gregor Wolf |