Skip to content

Commit

Permalink
Merge pull request #43 from boschrexroth/bugfix/ipv6
Browse files Browse the repository at this point in the history
Bugfix/ipv6
  • Loading branch information
Sebastian Krauskopf committed May 5, 2022
2 parents 05f73cf + 445d6f4 commit d91816f
Show file tree
Hide file tree
Showing 17 changed files with 516 additions and 463 deletions.
48 changes: 25 additions & 23 deletions .eslintrc.js
@@ -1,25 +1,27 @@
module.exports = {
"env": {
"commonjs": true,
"es6": true,
"node": true,
"mocha": true,
"browser": false
},
"extends": [
"eslint:recommended",
"plugin:mocha/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
},
"plugins": [
"mocha"
]
"env": {
"commonjs": true,
"es6": true,
"node": true,
"mocha": true,
"browser": false
},
"extends": [
"eslint:recommended",
"plugin:mocha/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
'spaced-comment': 'error'
},

"plugins": [
"mocha"
]
};
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -97,7 +97,8 @@ Any use of the source code and related documents of this repository in applicati
* 2022-03-14: 1.8.15 - fix: make subscribe node useable on other ports than 443 (Bug457112).
fix: remove an uncaught exception which was introduced with version 1.8.14 (Bug454078).
* 2022-04-26: 1.8.16 - fix: possible connection break on heavy load for commands: create, delete, write.

* 2022-05-28: 1.8.17 - feat: added support for IPv6.

## About

Copyright © 2020-2022 Bosch Rexroth AG. All rights reserved.
Expand Down
67 changes: 54 additions & 13 deletions lib/CtrlxCore.js
Expand Up @@ -57,9 +57,9 @@ const STATE_AUTHENTICATING = 2;
*
* ctrlx.logIn()
* .then(() => ctrlx.datalayerRead('framework/bundles/com_boschrexroth_comm_datalayer/active') )
* .then((data) => console.log(data))
* .then((data) => debug(data))
* .then(() => ctrlx.datalayerRead('framework/metrics/system/cpu-utilisation-percent') )
* .then((data) => console.log(data))
* .then((data) => debug(data))
* .catch((err) => console.error('Housten we are in trouble: ' + err))
* .finally(() => ctrlx.logOut());
*
Expand All @@ -71,10 +71,10 @@ const STATE_AUTHENTICATING = 2;
* await ctrlx.logIn()
*
* let data1 = await ctrlx.datalayerRead('framework/bundles/com_boschrexroth_comm_datalayer/active');
* console.log(data1);
* debug(data1);
*
* let data2 = await ctrlx.datalayerRead('framework/metrics/system/cpu-utilisation-percent');
* console.log(data2);
* debug(data2);
*
* } catch(err) {
* console.error('Housten we are in trouble: ' + err);
Expand Down Expand Up @@ -112,6 +112,7 @@ class CtrlxCore {
this._timeout = -1;
this._autoReconnect = false;
this._servername = (net.isIP(this._hostname) === 0) ? this._hostname : '';
this._isIPv6 = net.isIPv6(this._hostname);
}


Expand Down Expand Up @@ -139,18 +140,54 @@ class CtrlxCore {
*/
static _parseHost(host) {

// Plain IPv4 or IPv6 (without port)?
let ipVersion = net.isIP(host);

// IPv4
if (ipVersion === 4) {
// just forward the host as hostname and default to https
return {
'hostname': host,
'port': 443
}
}

// IPv6
if (ipVersion === 6) {
// just forward the host as hostname and default to https
return {
'hostname': host.replace(/\[|\]/gi, ''), // Remove square brackets for IPv6
'port': 443
}
}

// Use a regular expression with 2 capture groups (hostname & port) to match
let reg = /([a-z0-9\-._~%]+|\[[a-z0-9\-._~%!$&'()*+,;=:]+\])(?::([0-9]+))?$/igm;
let m = reg.exec(host);

// [IPv6]
// The IPv6 address in the URL is enclosed by square brackets.
// For hostname, it does not need to be enclosed by square brackets.
// For example: // [fe80::202:55ff:fecf:50b]:9010 or //bjaix5:9010
//
// We have to remove the square brackets to prevent HTTP request error ENOTFOUND (-3008, getaddrinfo) for Linux targets

if (m !== null && m.length > 2) {
// Parsed result can be found in the capture groups
let hostname = m[1] || host;

// Check for IPv6
let possibleIPv6 = hostname.replace(/\[|\]/gi, '');
if (net.isIPv6(possibleIPv6)) {
hostname = possibleIPv6; // Remove square brackets for IPv6
}

return {
'hostname': m[1] || host,
'hostname': hostname,
'port': (m[2]) ? parseInt(m[2]) : 443
}
} else {
// In case the regexp fails, just forware the host as hostname and default to https
// In case the regexp fails, just forward the host as hostname and default to https
return {
'hostname': host,
'port': 443
Expand All @@ -170,7 +207,7 @@ class CtrlxCore {
static _parseJwt(token) {
let base64Url = token.split('.')[1];
let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
let jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
let jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));

Expand Down Expand Up @@ -200,12 +237,15 @@ class CtrlxCore {
password: password
});



let options = {
hostname: hostname,
servername: servername,
port: port,
path: '/identity-manager/api/v2/auth/token',
method: 'POST',

headers: {
'Connection': 'keep-alive',
'Content-Type': 'application/json',
Expand All @@ -222,11 +262,11 @@ class CtrlxCore {
let data = "";

res.setEncoding('utf8');
res.on('data', function (chunk) {
res.on('data', function(chunk) {
data += chunk;
});

res.on('end', function () {
res.on('end', function() {

// We expect statusCode 201 if authentication was successful.
if (res.statusCode === 201) {
Expand All @@ -238,7 +278,7 @@ class CtrlxCore {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down Expand Up @@ -272,6 +312,7 @@ class CtrlxCore {
port: port,
path: '/identity-manager/api/v2/auth/token',
method: 'DELETE',

headers: {
'Authorization': tokenType + ' ' + token,
'Connection': 'close',
Expand All @@ -295,7 +336,7 @@ class CtrlxCore {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down Expand Up @@ -386,7 +427,7 @@ class CtrlxCore {
this._authorization = undefined;
this._state = STATE_LOGGED_OUT;

debug('logIn() ERROR communication');
debug('logIn() ERROR communication', err);
reject(err);
return;
}
Expand Down Expand Up @@ -819,7 +860,7 @@ class CtrlxCore {
.catch((err) => { reject(err); })
.finally(() => { this._state = STATE_LOGGED_IN; });
} else {
debug(`datalayerSubscribe(${paths}) ERROR`);
debug(`datalayerSubscribe(${paths}) ERROR`, err);
reject(err);
}
} else {
Expand Down
12 changes: 8 additions & 4 deletions lib/CtrlxDatalayerSubscription.js
Expand Up @@ -26,6 +26,7 @@
'use strict'

// @ts-ignore
const net = require('net');
const EventSource = require('launchdarkly-eventsource').EventSource;
const EventEmitter = require("events").EventEmitter;
const CtrlxDatalayer = require('./CtrlxDatalayerV2');
Expand Down Expand Up @@ -72,6 +73,7 @@ class CtrlxDatalayerSubscription extends EventEmitter {
super();

this._hostname = hostname;
this._isIPv6 = net.isIPv6(this._hostname);
this._port = port;
this._servername = servername;
this._authorization = authorization;
Expand Down Expand Up @@ -111,7 +113,9 @@ class CtrlxDatalayerSubscription extends EventEmitter {
}

// Arguments are given as query parameter.
let url = `https://${this._hostname}:${this._port}/automation/api/v2/events?nodes=${this._nodes}`;
// IPv6: We have to use IPv6 square brackets for valid url
let hostname = this._isIPv6 ? '[' + this._hostname + ']' : this._hostname;
let url = `https://${hostname}:${this._port}/automation/api/v2/events?nodes=${this._nodes}`;
if (typeof this._publishIntervalMs !== 'undefined') {
url += `&publishIntervalMs=${this._publishIntervalMs}`;
}
Expand All @@ -131,7 +135,7 @@ class CtrlxDatalayerSubscription extends EventEmitter {
headers: {
'Authorization': this._authorization, // forward the authorization token
},
errorFilter: function (e) { // note: will be called for e.type='error' as well as e.type='end'
errorFilter: function(e) { // note: will be called for e.type='error' as well as e.type='end'
// will try reconnect on true.
if (sub._noInternalReconnect) {
return false; // reconnect of lib is disabled. Has to be handled by caller
Expand Down Expand Up @@ -163,7 +167,7 @@ class CtrlxDatalayerSubscription extends EventEmitter {

// Called when creation of eventstream was successful
// @ts-ignore
es.onopen = (/*e*/) => {
es.onopen = () => {
debug('open() DONE');
this._es = es;

Expand Down Expand Up @@ -248,7 +252,7 @@ class CtrlxDatalayerSubscription extends EventEmitter {
close() {
debug('close()');
if (this._es) {
//this._es.removeEventListener('update');
// this._es.removeEventListener('update');
// @ts-ignore
this._es.onopen = undefined;
// @ts-ignore
Expand Down
24 changes: 12 additions & 12 deletions lib/CtrlxDatalayerV1.js
Expand Up @@ -102,11 +102,11 @@ class CtrlxDatalayer {
let data = "";

res.setEncoding('utf8');
res.on('data', function (chunk) {
res.on('data', function(chunk) {
data += chunk;
});

res.on('end', function () {
res.on('end', function() {

// We expect 200 or 201 on success
if (res.statusCode !== 200 && res.statusCode !== 201) {
Expand All @@ -130,7 +130,7 @@ class CtrlxDatalayer {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down Expand Up @@ -182,11 +182,11 @@ class CtrlxDatalayer {
let data = "";

res.setEncoding('utf8');
res.on('data', function (chunk) {
res.on('data', function(chunk) {
data += chunk;
});

res.on('end', function () {
res.on('end', function() {

// We expect 200 or 204 on success
if (res.statusCode !== 200 && res.statusCode !== 204) {
Expand All @@ -200,7 +200,7 @@ class CtrlxDatalayer {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down Expand Up @@ -260,11 +260,11 @@ class CtrlxDatalayer {
let data = "";

res.setEncoding('utf8');
res.on('data', function (chunk) {
res.on('data', function(chunk) {
data += chunk;
});

res.on('end', function () {
res.on('end', function() {

// We expect 200 on success
if (res.statusCode !== 200) {
Expand All @@ -286,7 +286,7 @@ class CtrlxDatalayer {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down Expand Up @@ -345,11 +345,11 @@ class CtrlxDatalayer {
let data = "";

res.setEncoding('utf8');
res.on('data', function (chunk) {
res.on('data', function(chunk) {
data += chunk;
});

res.on('end', function () {
res.on('end', function() {

// We expect 200 on success
if (res.statusCode !== 200) {
Expand All @@ -371,7 +371,7 @@ class CtrlxDatalayer {
});

req.on('timeout', () => {
req.abort();
req.destroy();
});

req.on('error', (err) => {
Expand Down

0 comments on commit d91816f

Please sign in to comment.