cancel
Showing results for 
Search instead for 
Did you mean: 

Unable to connect to on-premise destination from BTP Cloud Foundry app

omricohen
Explorer
0 Kudos

Hello Experts,

I've a multi target application which has the following modules:

1) SAPUI5 App - frontend

2) Node.js app with express server and 'sap-cf-axios' (acts as middleware)

3) Standalone approuter (the SAPUI5 is embedded in the resources and has entry to protect the service)

Application flow: UI5 calls node which calls service (on-premise destination using cloud connector)

xs-app.json of the approuter

{
    "welcomeFile": "/app/webapp/index.html",
    "authenticationMethod": "route",
    "sessionTimeout": 30,
    "logout": {
      "logoutEndpoint": "/do/logout",
      "logoutPage": "/"
    },
    "routes": [
      {
        "source": "^/app/(.*)$",
        "target": "$1",
        "localDir": "resources",
        "authenticationType": "xsuaa"
      },
      {
        "source": "^/srv/(.*)$",
      "target": "$1",
      "destination": "srv-api",
      "authenticationType": "xsuaa"   
   }         
    ]

UI5 url works:

url: <my-cf-approuter>/app/webapp/index.html


node js code (without destination - also works)

url: <my-cf-approuter>/srv/hello

const passport = require('passport');
const xsenv = require('@sap/xsenv');
const JWTStrategy = require('@sap/xssec').JWTStrategy;

const express = require('express');

const services = xsenv.getServices({ uaa:'project1-xsuaa' });

const app = express();

passport.use(new JWTStrategy(services.uaa));

app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));

const handleHello = async (req, res) => {
    console.log("-->"+req.user.id)
    res.send("Hello " + req.user.id);
    //res.send("Hello");
}
  
app.get("/hello", handleHello);

const port = process.env.PORT || 3000;
app.listen(port, function () {
  console.log('myapp listening on port ' + port);
});


The node.js app uses a destination called SAP_GWD for trying to connect to the on-premise backend (system is configured in both BTP & Cloud Connector by the BASIS team using basic authenticaton) .

The code that uses the destination does not work:

url: <my-cf-approuter>/srv/get

const passport = require('passport');
const xsenv = require('@sap/xsenv');
const JWTStrategy = require('@sap/xssec').JWTStrategy;

const express = require('express');
const SapCfAxios = require("sap-cf-axios").default;

const services = xsenv.getServices({ uaa:'project1-xsuaa' });

const app = express();

passport.use(new JWTStrategy(services.uaa));

app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));

const axios1 = SapCfAxios("SAP_GWD");

const handleGet = async (req, res) => {
  console.log("Calling destination"); // this line is written to the log
  const response = await axios1({
      method: "GET",
      url: "/sap/opu/data/my_service...",
      headers: {
      accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
      }
  });
  console.log(response);
  res.send(response.data);  

app.get("/get", handleGet);

const port = process.env.PORT || 3000;
app.listen(port, function () {
  console.log('myapp listening on port ' + port);
});

The error log entry in the approuter is:

VError: error while forwarding request to ***/get: socket hang up correlation_id = ***

Attached also mta.yaml file which uses the destination service & the connectivity service

ID: prj1
_schema-version: '3.1'
version: 1.0.0
description: "Project 1"
modules:
# --------------------  SERVICE -----------------------------
 - name: project1-service
# ------------------------------------------------------------
   type: nodejs
   path: srv
   build-parameters:
     ignore:
       - 'default-*.json'
       - .env
       - '*node_modules*'
       - package-lock.json
   provides:
     - name: srv-api
       properties:
         srv-url: ${default-url}  
   requires:
     - name: project1-xsuaa
     - name: project1-destination-service
     - name: project1-connectivity-service
# ------------------- UI ------------------------
 - name: project1-app
# ------------------------------------------------------------
   type: html5
   path: app
   build-parameters:
     supported-platforms: []

# --------------------  APPROUTER -----------------------------
 - name: project1-approuter
# ------------------------------------------------------------
   type: nodejs
   path: approuter
   requires:
     - name: project1-xsuaa
     - name: srv-api
       group: destinations
       properties:
         name: srv-api # must be used in xs-app.json as well
         url: ~{srv-url}
         forwardAuthToken: true
     - name: project1-destination-service
     - name: project1-connectivity-service
   build-parameters:
     requires:
       - name: project1-app
         artifacts:
           - ./*
         target-path: resources

resources:
# ------------------------------------------------------------
 - name: project1-xsuaa
# ------------------------------------------------------------
   type: org.cloudfoundry.managed-service
   parameters:
     service: xsuaa
     service-plan: application
     path: ./xs-security.json
     config:
       xsappname: 'project1-${space}'
       role-collections:
        - name: 'TechUserPOC-${space}'
          description: Technical User POC
          role-template-references:
          - $XSAPPNAME.TechUserPOC
# ------------------------------------------------------------
 - name: project1-destination-service
# ------------------------------------------------------------
   type: org.cloudfoundry.managed-service
   parameters:
     service: destination
     service-plan: lite
# ------------------------------------------------------------
 - name: project1-connectivity-service
# ------------------------------------------------------------
   type: org.cloudfoundry.managed-service
   parameters:
     service: connectivity
     service-plan: lite    

Please reply if you need any more info.

How can I solve this issue?

Regards,

Omri

Ulrich_Schmidt
Product and Topic Expert
Product and Topic Expert
0 Kudos

Can you check in the Cloud Cockpit, whether the connection test for this destination works? That way you can at least narrow down, whether the problem is in the Cloud side settings, or in the connection to the on-premises side.

omricohen
Explorer
0 Kudos

Hi Ulrich,

I forgot to mention that this is one of the first things I've checked.

Connection test for the destination runs successfully from cockpit.

gregorw
Active Contributor
0 Kudos

Can you please improve the formatting so it would be easier to read all the details?

omricohen
Explorer
0 Kudos

Hi Gergor,

Fixed the formatting (-:

Also, just for checking I added the 'SAP_GWD' destination as a new entry in the approuter (same result)

{
     "source": "^/direct/(.*)$",
     "target": "$1",
     "destination": "SAP_GWD",
     "authenticationType": "xsuaa"   
}      

The next step will be accessing it directly for debugging purpose (without approuter/protected url) and see how it goes

Regards,

Omri

omricohen
Explorer
0 Kudos
Solved, there was a misconfiguration in the corporate firewall

Accepted Solutions (0)

Answers (1)

Answers (1)

gregorw
Active Contributor
0 Kudos

I would suggest you either use SAP Cloud SDK for your backend requests as it would solve the issue to call the backend via destination / connectivity service.

Or it might be even easier to use CAP as it seems that your backend is an OData Service.

omricohen
Explorer
0 Kudos

Thanks.

I did additional test using curl and it worked:

curl SAP_GWD.dest/sap/opu/...

It means that the connection is configured correctly.

I'll check the SAP Cloud SDK option.

Regards,

Omri

omricohen
Explorer
0 Kudos

Hi,

Tried to use SAP Cloud SDK minimal test example - still no luck )-:

import { executeHttpRequest } from '@sap-cloud-sdk/http-client';
import { getDestination } from '@sap-cloud-sdk/connectivity';

async function minimalTest() {
  const destination = await getDestination({
    destinationName: YOUR_DESTINATION_NAME
  });
  if (destination) {
    destination.authTokens?.forEach(authToken => {
      if (authToken.error) {
        throw new Error(`Error in authToken ${authToken.error}`);
      }
    });
  } else {
    throw new Error('Destination is undefined.');
  }
  console.log('Destination Retrieved.');
  const response = await executeHttpRequest(destination, {
    method: 'get',
    url: YOUR_SERVICE_URL
  });
  if (!response.data) {
    throw new Error('No data returned');
  }
  console.log('Data Retrieved.');
}

Works with Northwind (Proxy type - Internet)

Does not work with SAP_GWD (Proxy type - OnPremise)

I get 'Destination Retrieved' in the log but no other messages after it (no data nor exception).

Attached also SAP_GWD Connection settings:

sap-gwd.png

I guess there is an issue with the connectivity service.

Since I'm using SAP Standard code I'll open a case in SAP but before, any other ideas?

Best Regards,

Omri

gregorw
Active Contributor
0 Kudos

Maybe you try with my sample app: HTML5UserAPIforCF

omricohen
Explorer
0 Kudos

Thanks Gregor.

I already saw you excellent repo and I'll try to run the code (-:

Regards,

Omri

omricohen
Explorer
0 Kudos

Hi Gergor,

Your code didn't work either on my environment.

I've manage to debug my code remotely and saw a 'clearer' error message:

Timed out waiting for tunnel to open for tunnelId account:///<<account number>>

From my understanding something is blocked between CF and OnPremise (maybe on Firewall level)

Also, I guess deploy and curl work from BAS since BAS has its own reverse proxy.

Regards,

Omri

gregorw
Active Contributor
0 Kudos

Hi Omri,

can you check in the subaccount where you've deployed the application if the Cloud Connector is actually connected?

CU
Gregor

omricohen
Explorer
0 Kudos

Hi,

You wrote: can you check in the subaccount where you've deployed the application if the Cloud Connector is actually connected?

How can I test it? the tests I did were from BAS/BTP side to cloud connector (I don't have access to the cloud connector and need to ask the BASIS team).

Attached screenshot of the Cloud Connectors screen from the BTP.

When I did curl (from BAS) to the OData service I asked them to check and they saw the OData payload in the logs (from CF runtime there is no payload nor error in the Cloud Connector logs).

I also sent the BASIS team the following link for checking Firewall settings

Some more info: up to now the organization uses BAS for developing/deploying UI5 (frontend apps) to OnPremise system (SAP_GWD).

My POC is for developing full-stack app that will be running on CF and will use the OnPremise system.

Does the fact the developing/deploying process from BAS (that uses the destination) works and CF runtime code that uses the same destination doesn't work mean something?

Am I right by assuming that BAS has some 'special' proxy which is different from the CF runtime for using the connectivity service?

Finally, your help and patience are much appreciated!

Regards

Omri

gregorw
Active Contributor
0 Kudos

I still urge you to test with HTML5UserAPIforCF. Because your code sample is not using any SAP Supported library. If a deployment from BAS in the same subaccount is possible, but connecting from an UI5 app running in an approuter that has bindings to destination and connectivity service isn't then I think you should contact SAP support to help you.

omricohen
Explorer
0 Kudos

Hi Gregor,

As I wrote, I started with sap-cf-axios (which is not standard) and moved to SAP cloud SDK (using Minimal Example) and also your sample code from the github repo).

Also, In some of my tests I removed the UI5/Approuter and tested only the service (it was easy to check because the destination uses Basic Authentication and not JWT).

In all three cases I got the same error (503) and couldn't connect to on-premise destination (internet destination works for all three cases).

I already contacted SAP and opened a case and I'm waiting for reply (I'll update this thread).

Regards,

Omri