1

I'm working on a little project at the moment, where I have to make a SOAP request to a SAP PCo Management Host Service.

I worked on a proof of concept and created a client side javascript document which worked totally fine. So I tried to implement this script in NodeJs on the server side. Therefor I created a web server with node-express and made the request with node-request, but if I send out the request to the PCo Management Host Services is the answer 500 Internal Server Error.

The PCo Management Host displays a raw http request for test purposes, which looks like this:

POST LINKTOHOST HTTP/1.1
User-Agent: PCo
SOAPAction: urn:sap.com:pco.management/PCoManagementService/GetAgentInstancesRuntimeInfo
Authorization: Basic ********
Content-Type: text/xml
Host: HOST
Content-Length: 300
Expect: 100-continue
Accept-Encoding: gzip, deflate

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:urns="urn:sap.com:pco.management"><soap:Body><urns:GetAgentInstancesRuntimeInfo xsi:nil="true" /></soap:Body></soap:Envelope>

I took this raw request and created my own. Here is my working javascript proof on concept:

var userName = prompt("Please Log in with your Windows User Account. Username:","");
var userPW  = prompt("Passwort: ", "");

reqAllAgentRuntimeInfo();
setInterval(function() {reqAllAgentRuntimeInfo()}, 5000);

 /*
 *
 *  Functions
 * 
 */ 

//Sends out the SOAP request via XMLHttpRequest
function reqAllAgentRuntimeInfo()
{
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open('POST', 'LINKTOHOST', true, userName, userPW);

    var soapRequest =
    '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:urns="urn:sap.com:pco.management">' +
        '<soap:Body>' +
            '<urns:GetAgentInstancesRuntimeInfo xsi:nil="true" />' +
        '</soap:Body>'+
    '</soap:Envelope>';

    xmlhttp.setRequestHeader('SOAPAction', 'urn:sap.com:pco.management/PCoManagementService/GetAgentInstancesRuntimeInfo');
    xmlhttp.setRequestHeader('Authorization', 'Basic');
    xmlhttp.setRequestHeader('Content-Type', 'text/xml');
    
    xmlhttp.send(soapRequest);

    xmlhttp.onreadystatechange = function()
    {
        //Test for Status Code 4 & 200 = OK
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
        {
            displayData(xmlhttp);
        }
    };
}

And then i created this server side NodeJs script, which doesn't work and i can't explain myself why.

const http = require('http'); // 1 - Import Node.js  modules
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const port = 50001;

app.use(bodyParser.urlencoded({ extended: true}));

app.get('/', (req, res) => 
{
    res.redirect('/logIn');
});

app.get('/logIn', (req, res) => 
{
    res.sendFile(__dirname + '/sites/logIn.html');
});

app.post('/logIn', (req, res) =>
{
    //Encode User Credentials
    var base64url = require('base64url');
    var credentials = 'Basic ' + base64url(req.body.userName + ':' + req.body.password);
    
    var request = require('request');
    let soapReq = 
    '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:urns="urn:sap.com:pco.management">' +
        '<soap:Body>' +
            '<urns:GetAgentInstancesRuntimeInfo xsi:nil="true" />' +
        '</soap:Body>'+
    '</soap:Envelope>';

    var options = 
    {
        url: 'LINKTOHOST',
        method: 'POST',
        body: soapReq,
        headers:
        {
            'Host': 'HOST',
            'Content-Length':soapReq.length,
            'Accept-Encoding': 'gzip,deflate',
            'User-Agent': 'PCo',
            'SOAPAction': "urn:sap.com:pco.management/PCoManagementService/GetAgentInstancesRuntimeInfo",
            'Authorization': credentials,
            'Content-Type': "text/xml"
        }
    };
    
    let callback = (error, response, body) => 
    {
        if(!error && response.statusCode == 200){
            console.log('PCo Communication raw result', body);
        }
        else
        {
            console.log('PCo Communication Error: ', response.statusCode, response.statusMessage);
        }
    }
    console.log(options);
    request(options, callback);
})

app.listen(port, () =>
{
    console.log(`Webserver listening at http://localhost:${port}`);
}); 

I would be glad if someone could help me.

Thank you.

JamesRyan
  • 21
  • 4

2 Answers2

1

special thanks to factorypolaris. It works now. Here is my code:

let soapReq = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:urns="urn:sap.com:pco.management">' +
        '<soap:Body>' +
            '<urns:GetAgentInstancesRuntimeInfo xsi:nil="true" />' +
        '</soap:Body>'+
    '</soap:Envelope>';

    var options = 
    {
        host: '127.0.0.1',
        auth: req.body.userName + ':' + req.body.password,
        method: 'POST',
        port: 55555,
        path: '/PCoManagement',
        //body: soapReq, -->*Remove this*
        headers:
        {
            'SOAPAction': "urn:sap.com:pco.management/PCoManagementService/GetAgentInstancesRuntimeInfo",
            'Content-Type': "text/xml"
        }
    };
    
    var req = http.request(options, function (response) {
        console.log('STATUS: ' + response.statusCode);
        console.log('HEADERS: ' + JSON.stringify(response.headers));
        response.on('data', function (chunk) {
            console.log('BODY: ' + chunk);
        });
    });
    
    req.write(soapReq); //--> Add this to write your actual data to the message

    req.end();  //--> Add end() to define the end of your request
JamesRyan
  • 21
  • 4
0

Your request( {options} ) are dated

The {options} your using for request() are no longer current. Newer versions of request() handle much of the heavy lifting for you. See the changes outlined below.

// REMOVE Immediate line break after =
let soapReq = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:urns="urn:sap.com:pco.management">' +
        '<soap:Body>' +
            '<urns:GetAgentInstancesRuntimeInfo xsi:nil="true" />' +
        '</soap:Body>'+
    '</soap:Envelope>';

In NODE, variable assignments cannot start with a line break.

// This will cause problems.
let x = 
''+'y';


// Do it like this is OK.
let x = ''+
'y';

var options = 
    {
        hostname: 'LINKTOHOST', // KEY NAME CHANGE: url -> hostname
        auth: 'user:password', //  Moved (No base64URLEncode())
        method: 'POST',
        body: soapReq,
        headers:
        {
            // 'Host': 'HOST', ---> REMOVE
            // 'Authorization': credentials, ---> Moved To Above             
            'Content-Length':soapReq.length, // May not be needed
            'Accept-Encoding': 'gzip,deflate', // May not be need
            'User-Agent': 'PCo',
            'SOAPAction': "urn:sap.com:pco.management/PCoManagementService/GetAgentInstancesRuntimeInfo",
            'Content-Type': "text/xml"
        } 
    };

request(options, function (response) {
                console.log('STATUS: ' + response.statusCode);
                console.log('HEADERS: ' + JSON.stringify(response.headers));
                response.on('data', function (chunk) {
                    console.log('BODY: ' + chunk);
                });
            });

*******
SEE The Commented Lines for edits.
*******

See a modern example

factorypolaris
  • 2,757
  • 12
  • 15
  • Thank you for your help. So i changed the edits into my code, but now the callback function does not work. It says this: *TypeError: Cannot read property 'statusCode' of undefined*. If i try to put out the value in the console it says *undefined*. – JamesRyan Aug 14 '20 at 13:20
  • So now there is another error. `STATUS: undefined` `HEADERS: undefined` `TypeError: response.on is not a function` – JamesRyan Aug 14 '20 at 14:12
  • Ensure options.headers.host is not defined. Try changing options.hostname to either "url" or "host". Ensure your able to access the URL via other methods to ensure the issue is not on the other end. – factorypolaris Aug 14 '20 at 14:12
  • I think you should look at ensuring the module your using is correct. Node V10+ has request built in through the http module. Take a look here and if your running a version of node new enough, make the change. Everything you have now should be compatible. See Here: https://nodejs.org/api/http.html#http_http_request_options_callback – factorypolaris Aug 14 '20 at 14:17