401 using hybrid relay connections with node.js - javascript

I was just running the sample code from Microsoft to test out hybrid relay connections using node.js
running:
node listener.js
producing the following error:
errorError: unexpected server response (401)
Here is my code (node listener.js)...
const WebSocket = require('hyco-ws');
const ns = "hcrelay.servicebus.windows.net";
const path = "hc1";
const keyrule = "hc1key";
const key = "Password#1234";
var wss = WebSocket.createRelayedServer(
{
server : WebSocket.createRelayListenUri(ns, path),
token: WebSocket.createRelayToken('http://' + ns, keyrule,key)
},
function (ws) {
console.log('connection accepted');
ws.onmessage = function (event) {
console.log(event.data);
};
ws.on('close', function () {
console.log('connection closed');
});
});
console.log('listening');
wss.on('error', function(err) {
console.log('error' + err);
});

I tried your code and it worked fine. The key point is that I set keyrule equal to the name of Shared access policy and set key equal to the primary key of Shared access policy.
const WebSocket = require('hyco-ws');
const ns = "hcrelay.servicebus.windows.net"; // Relay namespace
const path = "hc1"; // Hybrid connection name
const keyrule = "RootManageSharedAccessKey"; // Policy name
const key = "sjSqVUo..."; // Primary key
var wss = WebSocket.createRelayedServer(
{
server : WebSocket.createRelayListenUri(ns, path),
token: WebSocket.createRelayToken('http://' + ns, keyrule,key)
},
function (ws) {
console.log('connection accepted');
ws.onmessage = function (event) {
console.log(event.data);
};
ws.on('close', function () {
console.log('connection closed');
});
});
console.log('listening');
wss.on('error', function(err) {
console.log('error' + err);
});

Related

Websocket local ip and public ip

I have a webview android app that can connect to a server on local websocket and public websocket ip by redirect with no ip provider.
To work i have in my html webview app 2 js files and 2 html file. like this:
var ipValue;
var connection;
ipValue = "ws://192.168.1.40:81/";
connection = new WebSocket(ipValue);
console.log("IP value changed to:"+ipValue);
connection.onopen = function () {
connection.send('Websocket funcionando!' + new Date());
//connection.send('ping');
//await ws.send('2')
//ws.send("Hello, Ardunio");
};
connection.onerror = function (error) {
console.log('WebSocket Error ', error);
};
connection.onmessage = function (e) {
console.log('Server: ', e.data);
};
for the public
var ipValue;
var connection;
ipValue = "ws://xxxxxx.ddns.net:81/";
connection = new WebSocket(ipValue);
//console.log(text)
console.log("IP value changed to:"+ipValue);
connection.onopen = function () {
connection.send('Websocket funcionando!' + new Date());
//connection.send('ping');
//await ws.send('2')
//ws.send("Hello, Ardunio");
};
connection.onerror = function (error) {
console.log('WebSocket Error ', error);
};
how can i auto switch from my local ip to the public ip in the same js file. Or merge the 2 js files in one.

NestJS, SuperTest - Socket doesn't get event

I am struggling with my e2e test about my socket.
The socket connects and logs well to the NestJS Gateway, but it doesn't come into my listener bedRequest.
My test consists of sending a create request through supertest and at the end, the gateway broadcasts a message to connected sockets and I want to verify it.
In Gateway logs, I see the client connected, logIn, and disconnected if it can help.
Thank you in advance.
it("/POST bedRequest", (done) => {
let bedRequestCreate = some payload
let expectedResult = other payload
const address = app.getHttpServer().listen().address();
const baseAddress = `http://[${address.address}]:${address.port}`;
const client = io(`${baseAddress}/`);
client.on("connect", () => { // this works
client.emit("logIn", {access_token: accessToken}, (isConnected) => {
expect(isConnected).toBeTruthy(); // this works
client.on("bedRequest", (data) => { // this doesn't work
expect(JSON.parse(data)).toMatchObject({
siteId: bedRequestCreate.siteId,
...expectedResult
});
done();
});
});
});
return request(app.getHttpServer())
.post("/api/bedRequest/")
.send(bedRequestCreate)
.set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + accessToken)
.expect('Content-Type', /json/)
.expect(201)
.end((err, res) => {
if (err) return done(err);
expect(res.body.generatedMaps[0]).toMatchObject(expectedResult); // this works
});
});
Gateway :
#WebSocketGateway()
export class SocketGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
#WebSocketServer() server: Server;
wsClients = [];
private logger: Logger = new Logger('SocketGateway');
handleDisconnect(client: Client) {
for (let i = 0; i < this.wsClients.length; i++) {
if (this.wsClients[i] === client) {
this.wsClients.splice(i, 1);
break;
}
}
this.logger.log(`Client disconnected: ${client.id}`);
}
broadcast(event: String, message: any) {
const broadCastMessage = JSON.stringify(message);
for (let c of this.wsClients) { // sends to the right client
c.send(event, broadCastMessage);
}
}
#UseGuards(WebSocketJwtAuthGuard)
#SubscribeMessage("logIn")
handleLogIn(client) { // works
this.logger.log(`Socket Client connected : ${client.id} for user ${client.user.id} / ${client.mail}`);
this.wsClients.push(client);
return true;
}
handleConnection(client) {
return `Client connected: ${client.id}`;
}
afterInit() {
this.logger.log("SocketGateway initialized")
}
}
Okay I found it...
It was because of this line : c.send(event, broadCastMessage);
I changed send by emit and it works fine.

Unable to create a stable websocket implementation

Usecase:
This runs on the server side (Keystone) of an Android application
App connects to the socket with the user's accesstoken
App shows indicators for all the other user's who are connected to the socket
When a user changes some data in the app, a force refresh is send over the socket to all the "online" users so that they know to fetch the latest data
Main problem:
It works until a client loses it's internet connection right in between the intervals. Then the socket connection is closed and not reopened.
I don't know if it's a problem with my implementation or a problem with implementation on the client side
Implementation uses:
https://github.com/websockets/ws
More specifically https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
Here is the implementation on the server:
const clients = {};
let wss = null;
const delimiter = '_';
/**
* Clients are stored as "companyId_deviceId"
*/
function getClients() {
return clients;
}
function sendMessage(companyId, msg) {
try {
const clientKey = Object.keys(clients).find((a) => a.split(delimiter)[0] === companyId.toString());
const socketForUser = clients[clientKey];
if (socketForUser && socketForUser.readyState === WebSocket.OPEN) {
socketForUser.send(JSON.stringify(msg));
} else {
console.info(`WEBSOCKET: could not send message to company ${companyId}`);
}
} catch (ex) {
console.error(`WEBSOCKET: could not send message to company ${companyId}: `, ex);
}
}
function noop() { }
function heartbeat() {
this.isAlive = true;
}
function deleteClient(clientInfo) {
delete clients[`${clientInfo.companyId}${delimiter}${clientInfo.deviceId}`];
// notify all clients
forceRefreshAllClients();
}
function createSocket(server) {
wss = new WebSocket.Server({ server });
wss.on('connection', async (ws, req) => {
try {
// verify socket connection
let { query: { accessToken } } = url.parse(req.url, true);
const decoded = await tokenHelper.decode(accessToken);
// add new websocket to clients store
ws.isAlive = true;
clients[`${decoded.companyId}${delimiter}${decoded.deviceId}`] = ws;
console.info(`WEBSOCKET: ➕ Added client for company ${decoded.companyId} and device ${decoded.deviceId}`);
await tokenHelper.verify(accessToken);
// notify all clients about new client coming up
// including the newly created socket client...
forceRefreshAllClients();
ws.on('pong', heartbeat);
} catch (ex) {
console.error('WEBSOCKET: WebSocket Error', ex);
ws.send(JSON.stringify({ type: 'ERROR', data: { status: 401, title: 'invalid token' } }));
}
ws.on('close', async () => {
const location = url.parse(req.url, true);
const decoded = await tokenHelper.decode(location.query.accessToken);
deleteClient({ companyId: decoded.companyId, deviceId: decoded.deviceId });
});
});
// Ping pong on interval will remove the client if the client has no internet connection
setInterval(() => {
Object.keys(clients).forEach((clientKey) => {
const ws = clients[clientKey];
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping(noop);
});
}, 15000);
}
function forceRefreshAllClients() {
setTimeout(function () {
Object.keys(clients).forEach((key) => {
const companyId = key.split(delimiter)[0];
sendMessage(companyId, createForcedRefreshMessage());
});
}, 1000);
}

Error promise after publish data to MQTT broker in My Alexa Lambda node js

I have problem with my Lambda, actually in promise nodejs. I have wrote code like this in my Lambda:
'use strict'
const Alexa = require('alexa-sdk');
const mqtt = require('mqtt');
const APP_ID = undefined;
const WELCOME_MESSAGE = 'Welcome to the lamp control mode';
const WELCOME_REPROMT = 'If you new please say help'
const HELP_MESSAGE = 'In this skill you can controlling lamp to turn off or on, dim the lamp, change the lamp color and schedule the lamp';
const STOP_MESSAGE = 'Thanks for using this skill, Goodbye!';
const OFF_RESPONSE = 'Turning off the lamp';
const ON_RESPONSE = 'Turning on the lamp';
const DIM_RESPONSE = 'Dimming the lamp';
const CHANGE_RESPONSE = 'Changing the lamp color';
const AFTER_RESPONSE = 'Wanna control something again ?';
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', WELCOME_MESSAGE, WELCOME_REPROMT);
},
'OnOffIntent' : function () {
var status = this.event.request.intent.slots.status.value;
var location = this.event.request.intent.slots.location.value;
console.log(status);
console.log(location);
if (status == 'on') {
// Promise Start
var mqttPromise = new Promise(function(resolve, reject) {
var options = {
port: '1883',
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
username: 'username',
password: 'password',
};
var client = mqtt.connect('mqtt://broker-address', options)
client.on('connect', function() {
client.publish("lamp/status", status + ' ' + location, function() {
console.log("Message is published");
client.end();
resolve('Done Sending');
});
});
});
mqttPromise.then(
function(data) {
console.log('Function called succesfully', data);
this.emit(':ask', ON_RESPONSE, AFTER_RESPONSE);
}, function(err) {
console.log('An error occurred: ', err);
}
);
// Promise END
// this.emit(':ask', ON_RESPONSE, AFTER_RESPONSE);
// client.publish("lamp/status", status + ' ' + location);
} else if (status == 'off') {
this.emit(':ask', OFF_RESPONSE, AFTER_RESPONSE);
// client.publish("lamp/status", status + ' ' + location);
}
},
'DimIntent' : function () {
// to do here
},
'ChangeColorIntent' : function () {
// to do here
},
'ShceduleIntent' : function () {
// to do here
},
'AMAZON.HelpIntent': function () {
this.emit(':ask', HELP_MESSAGE, 'Wanna control something ?');
},
'AMAZON.StopIntent': function () {
this.emit(':tell', STOP_MESSAGE);
}
};
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
}
I test my code with Service Simulator in Alexa Developer and get this result :
Result Image
So I checked output in Lambda and I got this error report :
Error in Lamda
Can anyone please help me? I have no idea with this because this is my first trial :)
The crux of your error seems to be this specific line in the log:
Cannot read property 'emit' of undefined
And after following the flow of your program, it's likely ocurring here:
mqttPromise.then(
function(data) {
console.log('Function called succesfully', data);
// It's probably ocurring in this line below
this.emit(':ask', ON_RESPONSE, AFTER_RESPONSE);
}, function(err) {
console.log('An error occurred: ', err);
}
)
The log is saying that you tried using this, it's undefined and doesn't have an emit property. Thats ocurring because of how this works in Js. You could workaround this problem by saving a reference to this
var that = this;
var mqttPromise = new Promise(function(resolve, reject) {
var options = {
port: '1883',
clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8),
username: 'username',
password: 'password',
};
var client = mqtt.connect('mqtt://broker-address', options)
client.on('connect', function() {
client.publish("lamp/status", status + ' ' + location, function() {
console.log("Message is published");
client.end();
resolve('Done Sending');
});
});
});
mqttPromise.then(
function(data) {
console.log('Function called succesfully', data);
that.emit(':ask', ON_RESPONSE, AFTER_RESPONSE);
}, function(err) {
console.log('An error occurred: ', err);
}
);
I would also recommend reading up a bit on "How 'this' works in Javascript"
MDN
Stack Overflow - "how does 'this' work"

Using Node.js to connect to a REST API

Is it sensible to use Node.js to write a stand alone app that will connect two REST API's?
One end will be a POS - Point of sale - system
The other will be a hosted eCommerce platform
There will be a minimal interface for configuration of the service. nothing more.
Yes, Node.js is perfectly suited to making calls to external APIs. Just like everything in Node, however, the functions for making these calls are based around events, which means doing things like buffering response data as opposed to receiving a single completed response.
For example:
// get walking directions from central park to the empire state building
var http = require("http");
url = "http://maps.googleapis.com/maps/api/directions/json?origin=Central Park&destination=Empire State Building&sensor=false&mode=walking";
// get is a simple wrapper for request()
// which sets the http method to GET
var request = http.get(url, function (response) {
// data is streamed in chunks from the server
// so we have to handle the "data" event
var buffer = "",
data,
route;
response.on("data", function (chunk) {
buffer += chunk;
});
response.on("end", function (err) {
// finished transferring data
// dump the raw data
console.log(buffer);
console.log("\n");
data = JSON.parse(buffer);
route = data.routes[0];
// extract the distance and time
console.log("Walking Distance: " + route.legs[0].distance.text);
console.log("Time: " + route.legs[0].duration.text);
});
});
It may make sense to find a simple wrapper library (or write your own) if you are going to be making a lot of these calls.
Sure. The node.js API contains methods to make HTTP requests:
http.request
http.get
I assume the app you're writing is a web app. You might want to use a framework like Express to remove some of the grunt work (see also this question on node.js web frameworks).
/*Below logics covered in below sample GET API
-DB connection created in class
-common function to execute the query
-logging through bunyan library*/
const { APIResponse} = require('./../commonFun/utils');
const createlog = require('./../lib/createlog');
var obj = new DB();
//Test API
routes.get('/testapi', (req, res) => {
res.status(201).json({ message: 'API microservices test' });
});
dbObj = new DB();
routes.get('/getStore', (req, res) => {
try {
//create DB instance
const store_id = req.body.storeID;
const promiseReturnwithResult = selectQueryData('tablename', whereField, dbObj.conn);
(promiseReturnwithResult).then((result) => {
APIResponse(200, 'Data fetched successfully', result).then((result) => {
res.send(result);
});
}).catch((err) => { console.log(err); throw err; })
} catch (err) {
console.log('Exception caught in getuser API', err);
const e = new Error();
if (err.errors && err.errors.length > 0) {
e.Error = 'Exception caught in getuser API';
e.message = err.errors[0].message;
e.code = 500;
res.status(404).send(APIResponse(e.code, e.message, e.Error));
createlog.writeErrorInLog(err);
}
}
});
//create connection
"use strict"
const mysql = require("mysql");
class DB {
constructor() {
this.conn = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'pass',
database: 'db_name'
});
}
connect() {
this.conn.connect(function (err) {
if (err) {
console.error("error connecting: " + err.stack);
return;
}
console.log("connected to DBB");
});
}
//End class
}
module.exports = DB
//queryTransaction.js File
selectQueryData= (table,where,db_conn)=>{
return new Promise(function(resolve,reject){
try{
db_conn.query(`SELECT * FROM ${table} WHERE id = ${where}`,function(err,result){
if(err){
reject(err);
}else{
resolve(result);
}
});
}catch(err){
console.log(err);
}
});
}
module.exports= {selectQueryData};
//utils.js file
APIResponse = async (status, msg, data = '',error=null) => {
try {
if (status) {
return { statusCode: status, message: msg, PayLoad: data,error:error }
}
} catch (err) {
console.log('Exception caught in getuser API', err);
}
}
module.exports={
logsSetting: {
name: "USER-API",
streams: [
{
level: 'error',
path: '' // log ERROR and above to a file
}
],
},APIResponse
}
//createlogs.js File
var bunyan = require('bunyan');
const dateFormat = require('dateformat');
const {logsSetting} = require('./../commonFun/utils');
module.exports.writeErrorInLog = (customError) => {
let logConfig = {...logsSetting};
console.log('reached in writeErrorInLog',customError)
const currentDate = dateFormat(new Date(), 'yyyy-mm-dd');
const path = logConfig.streams[0].path = `${__dirname}/../log/${currentDate}error.log`;
const log = bunyan.createLogger(logConfig);
log.error(customError);
}
A more easy and useful tool is just using an API like Unirest; URest is a package in NPM that is just too easy to use jus like
app.get('/any-route', function(req, res){
unirest.get("https://rest.url.to.consume/param1/paramN")
.header("Any-Key", "XXXXXXXXXXXXXXXXXX")
.header("Accept", "text/plain")
.end(function (result) {
res.render('name-of-the-page-according-to-your-engine', {
layout: 'some-layout-if-you-want',
markup: result.body.any-property,
});
});

Categories

Resources