Skip to Content
Technical Articles
Author's profile photo Max Lenk

How to use WebSockets in the Cloud Foundry Environment in the SAP Cloud Platform

Introduction

WebSockets are a pretty common way to enable applications to communicate fast with each other. Especially for Web Development they are essential, therefore it is important that they work sufficient and secure.

Usually they are straight forward and easy to use. But there is an aspect you need to pay attention to if you want to use them in the Cloud Foundry environment. If you don’t know about it, this might save you some time and nerves in the future.

SSL/TLS Termination

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.

In short words: You can use a secure WebSocket connection, even though you do not provide one yourself. The same works of course for a HTTPS connection.

What does this mean?

The WebSocket server needs to run on the port 8080 which is opened by Cloud Foundry to the internet.

Your WebSocket client needs to access the plain endpoint of your application without a specified port.

Some examples that do not work:

const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com:8080"

const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com:3000"

const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com:80"

Examples that will work:

const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com"

const url = "https://websocket-cf.cfapps.sap.hana.ondemand.com"
--> Only if there is not a http-based app running

const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com:443"
--> 443 is the only accessible port

Now, let’s try this.

Hands-on: WebSockets on Cloud Foundry

First of all you need a current version of NodeJS installed. If this is not the case, please refer to: https://nodejs.org/en/download/

Also you will need a SAP Cloud Platform Account in the Cloud Foundry environment.

Next, we are going to create script running on the Cloud Platform: server.js

const WebSocketServer = require('ws').Server
//We will create the websocket server on the port given by Cloud Foundry --> Port 8080
const ws = new WebSocketServer({ port: process.env.PORT || 8080 });

ws.on('connection', function (socket) {
  socket.send('Hi, this is the Echo-Server');
  socket.on('message', function (message) {
    console.log('Received Message: ' +  message);
    socket.send('Echo: ' + message);
  });
}); 

The script creates a WebSocket server which is listening for connections on the port specified by Cloud Foundry. Just in case, there is a fallback port specified. Once there is a connection established, the server sends a message to the connected client and waits for incoming messages. When a client sends a message the server responds with an echo.

Cloud Foundry also needs some meta information about the application. For this we need a file called package.json:

{
  "name": "websocket_cf",
  "version": "1.0.0",
  "description": "Basic WebSocket example application for Cloud Foundry on SAP Cloudplatform",
  "main": "client.js",
  "dependencies": {
    "ws": "^4.0.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node client.js"
  },
  "engines": {
    "node": "12.x.x"
  }
}

It stores information about how to start the application, what dependencies are needed, etc.

To install the dependencies run the following command:

> npm install

 

In order to deploy the app on the Cloud Foundry environment we need a manifest.yml:

---
applications:
- name: websocket_cf
  command: node server.js
  buildpack: https://github.com/cloudfoundry/nodejs-buildpack
  health-check-type: none
  memory: 256M
  random-route: true

Now you are ready to deploy the server script to the cloud!

For the next steps you need to have the Cloud Foundry Command Line Interface installed: https://github.com/cloudfoundry/cli#downloads

Now use your command line (CMD, Terminal, …) to navigate to the folder you created the files server.js, package.json and manifest.yml. 

> cd /folder/to/the/manifest/

Now set the API Endpoint according to the Cloud Foundry environment you are using. In my case it is: https://api.cf.sap.hana.ondemand.com

> cf api https://api.cf.sap.hana.ondemand.com
api endpoint:   https://api.cf.sap.hana.ondemand.com
api version:    2.100.0

Afterwards you need to login using your e-mail and password:

(You might need to choose the corresponding org where you want to deploy the app)

> cf login

After a successful login you can push the app to the cloud:

> cf push

This might take a minute, but once it’s done you should see something like this:

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: websocket-cf.cfapps.sap.hana.ondemand.com
last uploaded: Tue Jan 23 09:30:51 UTC 2018
stack: cflinuxfs2
buildpack: https://github.com/cloudfoundry/nodejs-buildpack

     state     since                    cpu    memory          disk          details
#0   running   2018-01-23 10:31:24 AM   0.0%   15.7M of 256M   56.4M of 1G

The most important information is the url: “urls: websocket-cf.cfapps.sap.hana.ondemand.com”. You will need it to connect the client.

Alright, now that the server is running we need a client to test it: client.js

const WebSocket = require('ws');

//replace the url with yours after pushing the app wss://<your-app>.<your-host>.hana.ondemand.com/
//do not specify a port
//you can use secure websockets because of the SSL-Termination
const url = "wss://websocket-cf.cfapps.sap.hana.ondemand.com"
const ws = new WebSocket(url);

ws.on('open', function() {
  const message = 'Hi there!'
  ws.send(message);
  console.log('Sent Message: '+ message)
});
ws.on('error',function(data){
  console.log('Error: ' + data);
});
ws.on('message', function(data, flags) {
  console.log('Recieved Message: ' + data);
});
ws.on('close', function() {
  console.log('Disconnected from Server');
}); 

The client connects to the server and sends a message “Hi there”. In case it gets a message from the server, it logs it to console. Same happens if there is an error or the connection is closed. Don’t forget to replace the url with yours.

To start the script run the following command:

> node client.js

You should get the following output:

Sent Message: Hi there!
Recieved Message: Hi, this is the Echo-Server
Recieved Message: Echo: Hi there!

Well that’s it. You have create a simple WebSocket Server and deployed on the SAP Cloud Platform using Cloud Foundry and NodeJS.

If you are lazy, you can also just download the following repository from GitHub, it contains all the necessary files.

For information please refer to:

https://github.com/websockets/ws

https://docs.cloudfoundry.org/

Assigned Tags

      17 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ankit .
      Ankit .

      Thanks, this is really helpful.

      Can you share how would the manifest file look in case a separate application on CF (UI Client) needs to communicate to the websocket server.

      Author's profile photo Noemi Bugyi
      Noemi Bugyi

      Hello,

      Thank you this is really useful!

      Could you please help with some ideas, hints regarding a connection issue.

      When the external client tries to connect to my Websocket server, the connection fails and I'm getting the "Upgrade required" - 426 error.

      Could you tell me which configuration should be changed, in order to establish the connection.

      Thank you in advance.

       

      Regards,

      Noemi B.

      Author's profile photo Max Lenk
      Max Lenk
      Blog Post Author

      Hi,

       

      did you use the samples provided here? Or are you using different code snippets?
      From the error itself it seems that you are trying to make a plain HTTP Call to the WebSocket server.
      https://httpstatuses.com/426

       

      Kind Regards,

      Max

      Author's profile photo Noemi Bugyi
      Noemi Bugyi

      Hello,

       

      I used the samples provided here.

      Thank you.

       

      Regards,

      Noemi B.

      Author's profile photo Noemi Bugyi
      Noemi Bugyi

      Hello,

       

      It is possible to remove the “Upgrade” header in order to establish the connection/handshake or how we can add custom headers to our websocket server ?

       

      Are there any websocket compatible libraries with cloud foundry ?

       

      Regards,

      Noemi B

      Author's profile photo George Adrian Oprea
      George Adrian Oprea

       

      Hello,

       

      I have also used this code as it is presented in the tutorial. An external client tried to connect through TCP on port 443 to the server hosted in SCP and in the logs I see the 426 - Upgrade required error.

      The client is a GPS Forwarding tool and is not written by me.

       

      Regards,

      George

      Author's profile photo Marcus Schiffer
      Marcus Schiffer

      Hi,

       

      this does not seem to work with Python and Flask. The app does not start.

      We use:

      port = int(os.environ.get('PORT', 8080))

      socketio.run(app, port=port)

      On Cloud Foundry we get the following error:

      instance: 871f54ea-b0ff-4653-7c84-51f2, index: 0, cell_id: 1aafb73f-4bfc-4187-b6ec-70605827cda2, reason: CRASHED, exit_description: Instance never healthy after 3m0s: Failed to make TCP connection to port 8080: connection refused, crash_count: 1, crash_timestamp: 1551966515081676000, version: 388df7a0-b416-490d-8627-c0003be1c7ce

      instance: 871f54ea-b0ff-4653-7c84-51f2, index: 0, cell_id: 1aafb73f-4bfc-4187-b6ec-70605827cda2, exit_description: Instance never healthy after 3m0s: Failed to make TCP connection to port 8080: connection refused, reason: CRASHED

       

      Any help is appreciated.

      Author's profile photo Max Lenk
      Max Lenk
      Blog Post Author

      Hi Marcus,

      your current issue is caused by the health check performed by Cloudfoundry. You can fix this by adding "health-check-type: none" to your manifest.yml. 

      Kind Regards,

      Max

      Author's profile photo Marcus Schiffer
      Marcus Schiffer

      Hi Max,

      thnks. the error resulted from a missing ” host=’0.0.0.0′ ” in the socketio.run .

      Then it works fine.

      However, the next issue arises:

      We want to secure the app with xsuaa and the approuter.

      While I can configure the route for the static files, i wonder how will the xs-app.json for the approuter look like when I want to pass the websockets requests ? In the flask app, only the method for the html template site is secured by the command:

      if ‘authorization’ not in request.headers:
      abort(403)

       

      However when I run the app on the CF, I see the websocket is never connected.

      in html this is never showing status "connected":

      var socket = io.connect(‘http://’ + document.domain + ‘:’ + location.port);
      socket.on(‘connect’, function () {
      socket.emit(‘conn’, { data: ‘I\’m connected!’ });
      });
      Maybe I do not understand the role of xs-app routes, or it is an issue with the document.domain (which points to approuter not to the app destination).
      Any help here is appreciated!
      Author's profile photo Zhounan Ye
      Zhounan Ye

      Hi author,

       

      I tired to create socket server and connect it on browser. It never works and get 502 error.

      I was wondering if the port must be 8080 when creating a websocket server in cloud foundry? I didn't managed to create a socket server with port 8080 coz the port is already in use.

      Victor

      Author's profile photo Max Lenk
      Max Lenk
      Blog Post Author

      Hi Victor,

       

      Cloud Foundry only exposes the port 8080 internally for applications so it has to be used. If the port is already in use in your application,do you mean you are running for example an express server on port 8080?

       

      Best regards,

      Max

      Author's profile photo Zhounan Ye
      Zhounan Ye

      Hi Max,

       

      Yeah, I created an express server but not with the port 8080 and also create a socket server with port 8080

      Author's profile photo Zhounan Ye
      Zhounan Ye

      Do u mind if we have a call and I will show u the code?

      Author's profile photo Zhounan Ye
      Zhounan Ye

      I tried your sample, and got an error when doing cf push "Error staging application: App staging failed in the buildpack compile phase"

      I don't know how to fix it~

       

       

      Author's profile photo Max Lenk
      Max Lenk
      Blog Post Author

      Would you be so kind to provide the whole error log? Is this a different error or is it related to your issue before?

      Author's profile photo Zhounan Ye
      Zhounan Ye

      No, it's not related to the question above

      Author's profile photo Bassel El-Bizri
      Bassel El-Bizri

      Excellent work Max ! 🙂

       

      Took me 2 days to figure out that it's impossible to get the server URL via the destinations, and I was including the port like a dummy

       

      Thanks !