How do I send messages to RabbitMQ Queue, using nodejs - javascript

I am currently building a load balancing tool to take different stress measurements of how servers handle http protocol requests. Currently, I can send just a ton, but I cannot process them all. That is why I am looking to put it all into a message queue like rabbit, using AMQP.
My current code https://github.com/yugely/Stork
I am currently using an event loop and a timeout to adequately do what I am intending to accomplish.
I want to use RabbitMQ by using one of my current loggers to "emit" a message into the message queue. I don't know how to modularize it so I don't have to constantly create channels as all the tutorials seem to just copy paste each other without going into how to use it in external files.
I'm hoping someone can either lead me to what I may be duping. I'm unsure of how to even ask this question. I have been looking at RabbitMQ and AMQP to handle a message queue for a project. The issue I am facing is that I don't know how to send a message to rabbit mq. Let me illustrate what I am understanding by doing a copypasta of the first tutorial on the rabbitmq site:
send.js
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var queue = 'hello';
var msg = 'Hello World!';
channel.assertQueue(queue, {
durable: false
});
/*
How do I do this outside the functions so receive gets it? How can I call a method/function to send a message to an existing queue through an existing channel?
*/
channel.sendToQueue(queue, Buffer.from(msg));
console.log(" [x] Sent %s", msg);
});
setTimeout(function() {
connection.close();
process.exit(0);
}, 500);
});
receive.js
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var queue = 'hello';
channel.assertQueue(queue, {
durable: false
});
console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);
/*
How do I set up a consumer outside this function?
*/
channel.consume(queue, function(msg) {
console.log(" [x] Received %s", msg.content.toString());
}, {
noAck: true
});
});
});
For the sender, do I just always create a new channel for every message?
All the tutorials accept arguments when you run the "node" terminal command. The only way I can currently see this working is to use the "child process" library, but that would be bad news bears right? As wouldn't that create another nodejs cluster?
If I have to use a client to send messages, am I able to use axios? (I've seen some people claiming they are able to but I don't know for sure). Since it will be using the amqp protocol, what is the amqp client?
Or are these queues like instantiated in the entry file? Like you set up the queue when you run your entry point command and then allow the different "events" to send messages to the queue?
How can I modularize this?
Just to illustrate, here is my current axios code
RadioFlyer.js
await axios(flight.journal.actions[modkey]).then(function (response) {
reaction.key.type = messageType.HTTPResponse.Okay
reaction.message = response === undefined ? response : "no response"
let smaug = typeof reaction.message.status === "undefined" ? reaction.message : reaction.message.status
flight.journal.reactions.push(reaction)
let pulse = {
id: flight.id + "-" + index,
timestamp: Date.now(),
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Okay,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
/*
Can I emit a messaging event to my logger
*/
//emit
seidCar.HTTPLogger.emit("logHttp", reaction)
//emit
seidCar.HeartbeatLogger.emit("pulse", pulse)
}).catch(function (error) {
reaction.key.type = messageType.HTTPResponse.Error
reaction.message = error.response === undefined ? error.code : error.response
let smaug = typeof reaction.message.status === "undefined" ? reaction.message : reaction.message.status
let pulse = {
id: flight.id + "-" + index,
timestamp: Date.now(),
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Error,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
let err = {
id: flight.id+"-"+index+"-ERROR",
timestamp : Date.now(),
fatal : false,
potentialFix : "Examine Http Call with id: " + flight.id + "-" + index,
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Error,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
flight.journal.reactions.push(reaction)
//emit
seidCar.HTTPLogger.emit("logHttp", reaction)
//emit
seidCar.ErrorLogger.emit("logError", err)
//emit
seidCar.HeartbeatLogger.emit("pulse", pulse)
})
And have my logger handler the sending to the queue?
HTTPLogger.js
/*
Can I now send the message to the queue here, in this file?
*/
const HTTPEventLogger = require("events")
const emitter = new HTTPEventLogger()
const errorEmitter = require("./ErrorLogger").Emitter
class HTTPLogger extends HTTPEventLogger {
logHttp(message) {
switch (message.key.type) {
case "ERROR":
if (message !== undefined) {
console.log(message)
} else {
errorEmitter.emit("tossIt", {
error:"HTTP error Message is undefined in ~/Homestasis/Agent/HTTPLoggerjs.",
poi:"check for recent additions to pilot agents in ~/Pilots/Agent",
timestamp: Date.now(),
potentialFix:"look to where you are emitting the message. Function Scope"
})
}
break;
case "OKAY":
if (message !== undefined) {
console.log(message)//bState.message.status)
} else {
errorEmitter.emit("tossIt", {
error:"HTTP okay Message is undefined in ~/Homestasis/Agent/HTTPLoggerjs.",
poi:"check for recent additions to pilot agents in ~/Pilots/Agent",
timestamp: Date.now(),
potentialFix:"look to where you are emitting the message. Function Scope"
})
}
break;
default:
errorEmitter.emit("tossIt", "this is a tossIt error log. No http key type was caught bSate = " + bState)
}
}
}
var logger = new HTTPLogger()
emitter.on("logHttp",logger.logHttp)
exports.Emitter = emitter
Thats how I'd like to send the message.
I'd like to be able to receive it much the same way
I'm not sure how to implement it given how I perceive it currently working and I am missing a key part of the equation. Thank you for your time!

I decided to not use RabbitMQ.
For node, RSMQ is what I am going with. I realized I fundamentally needed to shift how I viewed message queues working practically to get them to work practically. I am using RSMQ, as I can understand how it fits into how I built my project out. I was going with ZeroMQ at first (I still may, the pliability of sockets on steroids). But, I do like the actual features and stuff when I actually think about using it like a "mainline nerve".
The way I want to build it out goes as such:
Axios Makes the call, fires off an event to the httplogger which fires off an event to the message queue. So:
RadioFlyer.js > HttpLogger.js > RSMQ-Messagequeue.js > next things > and next...
RSMQ-Messagequeue.js acts as a "middle man" or an intermediary to put the stops on the wheels when overloaded.
The pros of RSMQ is that I also intend to implement a Redis Cache (I could implement ZeroMQ and do much the same), but I like that RSMQ allows for more queue options, instead of 1 queue with many topics.
There is a more fleshed out answer.

Related

How do I Collect User IDs + Retrieve Corresponding Tokens + Send a Push Notification Via Firebase Cloud Function (JS)

The Problem:
I have been unable to use Firebase (Google) Cloud Functions to collect and utilize device tokens for the cloud messaging feature.
Context:
I am a self-taught android-Java developer and have no JavaScript experience. Despite that, I believe I have code that should work and am not sure what the problem is. To my understanding, it could be one of three things:
Somehow my Firebase Realtime Database references are being called incorrectly and I am not retrieving data as expected.
I may need to use Promises to wait for all calls to be made before proceeding, however I don't really understand how I would incorporate that into the code I have.
I may be using multiple return statements incorrectly (which I am also fuzzy on).
My error message on the Firebase Realtime Database console is as follows:
#firebase/database: FIREBASE WARNING: Exception was thrown by user callback. Error: Registration token(s) provided to sendToDevice() must be a non-empty string or a non-empty array.
at FirebaseMessagingError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseMessagingError (/srv/node_modules/firebase-admin/lib/utils/error.js:254:16)
at Messaging.validateRegistrationTokensType (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:729:19)
at Messaging.sendToDevice (/srv/node_modules/firebase-admin/lib/messaging/messaging.js:328:14)
at admin.database.ref.once.snapshot (/srv/index.js:84:12)
at onceCallback (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:4933:51)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:4549:22
at exceptionGuard (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:698:9)
at EventList.raise (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:9684:17)
The above indicates I am not retrieving data either at all or by the time the return is called. My JavaScript function code is:
'use strict';
const admin = require('firebase-admin');
admin.initializeApp();
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushID}').onCreate((snapshot, context) => {
const valueObject = snapshot.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
sendNotificationPayload(valueObject.uid, payload);
}
index++;
});
});
});
function sendNotificationPayload(uid, payload){
admin.database()
.ref(`/User Token Data/${uid}`)
.once('value', snapshot=> {
var tokens = [];
//if(!snapshot.exists())return;
snapshot.forEach(item =>{
tokens.push(item.val())
});
admin.messaging()
.sendToDevice(tokens, payload)
.then(res => {
return console.log('Notification sent')
})
.catch(err => {
return console.log('Error in sending notification = '+err)
});
});
}
This code is mostly inspired by what was said to be a working example here from another Stack Overflow question here. I have successfully tested sending a notification to a single device by manually copying a device token into my function, so the function does run to completion. My Java code seems to be irrelevant to the problem, so I have not added it (please ask in the comments if you would like it added for further context).
What I Have Tried:
I have tried implementing promises into my code, but I don't think I was doing it properly. My main reference for this was here. I have also looked at the documentation for literally everything related to this topic, however my knowledge of JS is not sufficient to really apply barebones examples to my code.
My Firebase Realtime Database Nodes:
#1: Loop through chat members to collect user IDs:
"Chat Basics" : {
"1607801501690_TQY41wIfArhHDxEisyupZxwyHya2" : {
"Chat Users" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : true,
"TQY41wIfArhHDxEisyupZxwyHya2" : true
},
#2: Collect user tokens from collected IDs (ignore that tokens are matching):
"User Token Data" : {
"JXrclZuu1aOwEpCe6KW8vSDea9h2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi",
"TQY41wIfArhHDxEisyupZxwyHya2" : "duDR3KH3i3I:APA91bH_LCeslZlqL8akYw-LrM9Dv__nx4nU1TquCS0j6bGF1tlIARcheREuNdX1FheC92eelatBC8LO4t6gt8liRdFHV-NDuNLa13oHYxKgl3JBPPlrMo5rB5XhH7viTo4vfYOMftRi"
}
Conclusion:
Concrete examples would be much appreciated, especially since I am crunching right now. Thanks for your time and help!
Update:
After some more testing, it looks like the problem is definitely due to my lack of understanding of promises in two areas. Firstly, only one user is collected before the final return is called. Secondly, the final return is called before the 2nd forEach() loop can store snapshot data to an array.
For this code then, how may I modify (or rebuild) it so that it collects all keys before proceeding to retrieve token data from all keys - ultimately before returning the notification?
Just as with every question I post, I managed to figure out how to do it (tentatively) a few hours later. Below is a full example of how to send a notification to chat users based on a message sent (although it does not yet exclude the sender) to a given chat. The order of operations are as such:
User message is saved and triggers event. Relevant data the message contains are:
username, chat key, message
These are retrieved, with (username + message) as the (title + body) of the
notification respectively, and the chat key is used for user id reference.
Loop through chat user keys + collect.
Loop through array of chat user keys to collect array of device tokens.
Send notification when complete.
The code:
//Use firebase functions:log to see log
exports.pushNotification = functions.database.ref('/Chat Messages/{chatId}/{pushId}').onWrite((change, context) => {
const valueObject = change.after.val();
return admin.database().ref(`/Chat Basics/${valueObject.chatKey}/Chat Users`).once('value', statusSnapshot => {
var index = 0;
var totalkeys = statusSnapshot.numChildren();
var msgIDs = [];
statusSnapshot.forEach(msg=>{
msgIDs.push(msg.key.toString());
if(index === totalkeys - 1){
const payload = {
notification : {
title: valueObject.userName,
body: valueObject.message,
sound: "default"
}
}
let promises = [];
var tokens = [];
for(let i=0; i < msgIDs.length; i++){
let userId = msgIDs[i];
let promise = admin.database().ref(`/User Token Data/${userId}`).once('value', snapshot=> {
tokens.push(snapshot.val());
})
promises.push(promise);
}
return Promise.all(promises).then(() => {
return admin.messaging().sendToDevice(tokens, payload);
});
}
index++;
return false;
});
});
});

PUB/SUB Api doesn't send messages back, what might be the problem?

I have almost completed pub/sub fake-server, that requests user password and email (from the client), compares this info with database and returns data back. It has 'api_in' and 'api_out' frames and then the JSON.
The publisher takes and processes all the info without a hitch, but it doesn't seem to send anything back to the client (subscriber) and I don't know why, cause it is connected to the subsequent port.
And I know that this implementation is not a classic PUB/SUB pattern, but that was prerequisite, to do it like that.
I tried different pub/sub options, but nothing has changed.
Server
let zmq = require('zeromq');
const sqlite3 = require('sqlite3').verbose();
const DBSOURCE = "./db.sqlite";
let db = new sqlite3.Database(DBSOURCE, (err) => {
if(err) {
console.error(err.message);
throw err;
} else {
console.log('Connected to SQLite database');
db.run(`CREATE TABLE users (
user_id INTEGER,
email TEXT,
passw TEXT)`,
(err) => {
if (err) {
// Table already created
} else {
// Creating rows
let insert = 'INSERT INTO users (user_id, email, passw) VALUES (?,?,?)';
db.run(insert, [123098, 'phillCollins#gmail.com','5502013']);
db.run(insert, [42424242,'dukenukem3d#mustdie.com','RodriguesShallLiveLong']);
db.run(insert, [5,'yourchick#yandex.ru','semolinaAndPain666']);
}
})
}
});
const args = require('minimist')(process.argv.slice(2));
const pubSocket = zmq.socket('pub', null);
pubSocket.bindSync(`tcp://127.0.0.1:${args['pub']}`);
const subSocket = zmq.socket('sub', null);
subSocket.subscribe('api_in');
subSocket.on('message', function(data) {
let message = data.toString().replace(/api_in/g, '');
let mes = JSON.parse(message);
let api_out = 'api_out';
let errorWrongPWD = 'WRONG_PWD';
let errorWrongFormat = 'WRONG_FORMAT';
if(mes.type = 'login') {
db.get(`SELECT user_id from users WHERE email = ? and passw = ?`, [mes.email, mes.pwd], function(err, row) {
if(err) {
console.log(err);
} else {
if(row) {
let msg = {
msg_id: mes.msg_id,
user_id: row.user_id,
status: 'ok'
}
let outMessage = api_out + JSON.stringify(msg);
console.log(outMessage);
subSocket.send(outMessage);
} else {
let msg = {
msg_id: mes.msg_id,
status: 'error',
error: mes.email == '' || mes.pwd == '' ? errorWrongFormat : errorWrongPWD
}
console.log(msg);
let outMessage = api_out + JSON.stringify(msg);
subSocket.send(outMessage);
}
}
});
}
});
subSocket.bindSync(`tcp://127.0.0.1:${args['sub']}`);
client
let zmq = require('zeromq');
let uniqid = require('uniqid');
let readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
const args = require('minimist')(process.argv.slice(2));
const pubSocket = zmq.socket('pub', null);
let pubSocketTCP = `tcp://127.0.0.1:${args['sub']}`;
pubSocket.connect(pubSocketTCP);
const subSocket = zmq.socket('sub', null);
let subSocketTCP = `tcp://127.0.0.1:${args['pub']}`;
subSocket.connect(subSocketTCP);
let api_in = 'api_in';
let secondFrame = {
type: 'login',
email: '',
pwd: '',
msg_id: uniqid()
}
readline.question('What is your email? \n', (email) => {
secondFrame.email = email;
readline.question('What is your password \n', (pwd) => {
secondFrame.pwd = pwd;
let msg = api_in + JSON.stringify(secondFrame);
console.log(msg);
pubSocket.send(msg);
});
});
subSocket.subscribe('api_out');
subSocket.on('message', (response) => {
/* let res = response.toString().replace('api_out');
let responseParsed = JSON.parse(res);
console.log(responseParsed.status);
if(response.status == 'error') console.log(response.error); */
console.log(response);
});
I want the server side to send info back.
Well, first of all, welcome to the Zen-of-Zero domain. ZeroMQ is a powerful tool for smart signaling / messaging, so if you pay attention to all its internal beauties, there will be no such thing you will not be able to do with it ( nightmares still not avoided on this way forward ). If feeling to be new to this domain, one may enjoy a short read into "ZeroMQ Principles in less than Five Seconds" before diving into further details on subject, or re-use some of tricks posted here
Q : it doesn't seem to send anything back to the client (subscriber) and I don't know why
There are two pieces of code, that seem to use both PUB and SUB Scalable Formal Communication Pattern Archetypes, yet have some glitches on how these get configured.
The server-code :
seems to try to instantiate PUB-archetype and equips that instance with a single AccessPoint, using .bindSync()-method and cli-parameter args['pub'] for accepting connections over a plain and commontcp://-transport-class.
After defining the event-handler .on( 'message', ... ) for the second instance, being the SUB-archetype, this one becomes .bindSync()-equipped with a single AccessPoint, using tcp://-transport-class, for receiving connections using tcp://-transport-class.
If you indeed have to make a .send() over a SUB-alike archetype, you have to use XSUB alternative, where you can send data and perform some tricks with the actual payload on the PUB-side or XPUB-side ( ref. API documentation for details about ZMQ_XPUB_MANUAL mode capabilities and limits for some wilder data-mangling on the XPUB-side )
ZMQ_XSUB
Same as ZMQ_SUB except that you subscribe by sending subscription messages to the socket. Subscription message is a byte 1 (for subscriptions) or byte 0 (for unsubscriptions) followed by the subscription body. Messages without a sub/unsub prefix may also be sent, but have no effect on subscription status.
The client-code :
Seems to instantiate and .connect() both the client-local PUB and SUB Archetypes over tcp://-transport-class to the server-side AccessPoints ( which both ought to set ZMQ_LINGER to 0, so as to avoid infinitely hanging orphans ( version dependent defaults do not have other solution but an explicit setting on this ) ).
Possible improvements :
XPUB/XSUB with ZMQ_XPUB_MANUAL may solve the sending via SUB-archetype
XPUB/SUB with ZMQ_XPUB_MANUAL may solve the sending via SUB-archetype with less comfort of masquerading the messages to be sent via the .subscribe()-method
PUB/SUB with making all .send()-s strictly via a local PUB-archetype instance.
be explicit with ZMQ_SNDHWM and ZMQ_RCVHWM parameters if still loosing messages
be explicit on setting .subscribe() only after a successfully completed { .bind() + .connect() }-methods ( systematically using original zmq_errno() and zmq_strerror() functions for actively detecting and repairing the potential colliding states )
may request to work with only completed connections ( distributed systems have zero-warranty of an order of operations autonomous distributed (many) agents perform ) using .setsockopt( ZMQ_IMMEDIATE, 1 )
Your server is trying to send on the sub socket
subSocket.send(outMessage);
You can't send on the sub socket. It should be sending on a pub socket.

Example/Explanation on chrome.sockets.udp multicasting

I am making an Chrome app which will have a 'chat' feature (like what this app has). When a user joins the chat (by connecting to a port), the user would receive all messages that were created during his/her session. However, although I understand most of the app's code, I couldn't understand how the multicasting part of the code exactly works.
From what I understand, this is how it should go:
var socket = 0;
chrome.sockets.udp.create({bufferSize: 1024 * 1024}, function (createInfo) //Create socket entry
{
socket = createInfo.socketId;
console.log(socket); //This works - tells me an integer
var ttl = 12;
chrome.sockets.udp.setMulticastTimeToLive(socket, ttl, function (result)
{
if(result < 0)
{
console.log("MULTICAST FAILED" + result);
}
else
{
chrome.sockets.udp.bind(socket, "0.0.0.0", 8080, function(result) //Bind to socket
{
if(result < 0)
{
console.log("BIND FAILED" + result);
chrome.sockets.udp.close(socket);
}
else
{
chrome.sockets.udp.joinGroup(socket, "237.132.123.123", function(result) //join Multicast group
{
if(result < 0)
{
console.log("Couldn't join Group!");
chrome.sockets.udp.close(socket);
}
else
{
console.log("GOT HERE"); //Outputs this
chrome.sockets.udp.onReceive.addListener(function(msg) //Listen for receiving messages
{
console.log(msg.socketId);
})
chrome.sockets.udp.onReceiveError.addListener(function(error) //If error while receiving, do this
{
console.log(error.socketId + " " + error.resultCode);
});
}
})
}
});
}
})
})
//Later in the code (linked to a button I press (and when app closes)
chrome.sockets.udp.close(socket);
The problem with this is that although the code reaches the callback function of .send() (which should mean that it sent successfully), I also get errors while binding saying that the socket doesn't exist (although I created it and the socket number outputted an integer). When I make two application with the same code as above and try to make them listen/send to each other, neither gets the message.
Can someone provide a clear example (no gui code, no runtime messaging) which illustrates how to multicast correctly using the newer chrome.sockets.udp?
Just had to deal with this, your code should work if you include this in your manifest:
"sockets": {
"udp": {
"send": ["*"],
"bind": ["*"],
"multicastMembership": [""]
}
}
If you already did, try changing your port / address in case your network is already using them. If that still doesn't work, try removing the options on create() and see if anything happens.
Sorry if I'm too late.

chrome.hid.send fails on second use

Something about my use of chrome.hid.send seems to be leaving the bus in a bad state. I consistently can NOT get my second usage of the API call to work. Sometimes, it will also fail on the first usage. WITH THE EXACT SAME CODE, I can come back and try a while later (maybe 10min) and the first send will work.
The device I'm working with does not return a response to all messages sent to it. The test message for example, is just a dummy message that is ignored by the device. I've tested this both on a mac and a PC. My call stack depth is 2 at this point in my application (literally first one is kicked off by a button click and then a setTimeout calls the same method 5s later).
I've testing sending buffers of length 64Bytes as well as 58Bytes. The properties from the HidDeviceInfo object read "maxInputReportSize":64,"maxOutputReportSize":64
Params on first usage:
Params on second usage:
I really can't identify how I'm using the API incorrectly. When messages do succeed, I can see them on the device side.
// Transmits the given data
//
// #param[in] outData, The data to send as an ArrayBuffer
// #param[in] onTxCompleted, The method called on completion of the outgoing transfer. The return
// code is passed as a string.
// #param[in] onRxCompleted, The method called on completion of the incoming transfer. The return
// code is passed as a string along with the response as an ArrayBuffer.
send: function(outData, onTxCompleted, onRxCompleted) {
if (-1 === connection_) {
console.log("Attempted to send data with no device connected.");
return;
}
if (0 == outData.byteLength) {
console.log("Attempted to send nothing.");
return;
}
if (COMMS.receiving) {
console.log("Waiting for a response to a previous message. Aborting.");
return;
}
if (COMMS.transmitting) {
console.log("Waiting for a previous message to finish sending. Aborting.");
return;
}
COMMS.transmitting = true;
var dummyUint8Array = new Uint8Array(outData);
chrome.hid.send(connection_, REPORT_ID, outData, function() {
COMMS.transmitting = false;
if (onTxCompleted) {
onTxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '');
}
if (chrome.runtime.lastError) {
console.log('Error in COMMS.send: ' + chrome.runtime.lastError.message);
return;
}
// Register a response handler if one is expected
if (onRxCompleted) {
COMMS.receiving = true;
chrome.hid.receive(connection_, function(reportId, inData) {
COMMS.receiving = false;
onRxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '', inData);
});
}
});
}
// Example usage
var testMessage = new Uint8Array(58);
var testTransmission = function() {
message[0] = 123;
COMMS.send(message.buffer, null, null);
setTimeout(testTransmission, 5000);
};
testTranmission();
The issue is that Windows requires buffers to be the full report size expected by the device. I have filed a bug against Chromium to track adding a workaround or at least a better error message to pinpoint the problem.
In general you can get more detailed error messages from the chrome.hid API by enabling verbose logging with the --enable-logging --v=1 command line options. Full documentation of Chrome logging is here.

Strophe.js client connecting to server, disconnect/timeout

I have a little JavaScript XMPP client, written with Strophe, that connects to a server hosted on hosted.im. I think hosted.im uses ejabberd on their backend.
I establish the connection using
Strophe.Connection(myBoshService), and am able to send chat messages back and forth. However, after a certain time, it seems, there is an automatic disconnect if there is no activity.
Now, my question is, what would be a good way to keep the session active, so that it does not disconnect. Disconnect time seems to be very short, about 60 seconds or so.
Should I send some kind of activity back and forth to keep it open? Or, which seems simpler to me, should I somehow change the timout of the session. If so, where can I change this? Is this a server-setting, irregardless of the Strophe.Connection object, or can I set the timeout when initializing Strophe.Connection?
Thanks for any and all help.
Best regards,
Chris
Edit: Here is the code I use for connecting:
I manage the connection through a global variable Hello (yes, name is awkward, I took it from an example):
var Hello = {
connection: null,
start_time: null,
partner: {
jid: null,
name: null
},
log: function (msg) {
$('#log').append("<p>" + msg + "</p>");
},
send_ping: function (to) {
var ping = $iq({
to: to,
type: "get",
id: "ping1"}).c("ping", {xmlns: "urn:xmpp:ping"});
Hello.log("Sending ping to " + to + ".");
console.log("Sending ping to " + to + ".");
Hello.start_time = (new Date()).getTime();
Hello.connection.send(ping);
},
handle_pong: function (iq) {
var elapsed = (new Date()).getTime() - Hello.start_time;
Hello.log("Received pong from server in " + elapsed + "ms.");
console.log('Received pong from server in " + elapsed + "ms.');
$('#login').hide();
$('#chat').show();
//window.location = "chat.html";
//Hello.connection.disconnect();
return true;
},
//"<active xmlns="http://jabber.org/protocol/chatstates"/><body xmlns="http://jabber.org/protocol/httpbind">tuiuyi</body>"
displayIncomingText: function (text) {
var body = $(text).find("xml > body");
if (body.length === 0)
{
body = $(text).find('body');
if (body.length > 0)
{
body = body.text();
$('#chattext').append("<p>"+ body + "</p>");
}
else
{
body = null;
}
}
return true;
},
readRoster: function (iq) {
$(iq).find('item').each(function () {
var jid = $(this).attr('jid');
var name = $(this).attr('name') || jid;
Hello.partner.name = name;
Hello.partner.jid = jid;
});
return true;
}
};
The main relevant objects here are Hello.connect and Hello.partner, which stores the jid of the only person on the accounts roster, as this is a one to one chat.
Then, in $(document).ready, I bind two buttons to connect and send messages respectively:
$(document).ready(function () {
$('#chat').hide();
$('#chatSend').bind('click', function () {
Hello.connection.send(
$msg(
{to : Hello.partner.jid, type : 'chat'}
).c('body').t($('#chattextinput').val())
);
$('#chattext').append("<p align='right'>" + $('#chattextinput').val() + "</p>");
});
$('#SignIn').bind('click', function () {
$(document).trigger('connect', {
jid: $('#eMail').val(), password: $('#password_f').val()
}
);
});
});
Clicking the sign-in button triggers the event "connect":
$(document).bind('connect', function (ev, data) {
console.log('connect fired');
var conn = new Strophe.Connection("http://bosh.metajack.im:5280/xmpp-httpbind");
conn.connect(data.jid, data.password, function (status) {
console.log('callback being done');
if (status === Strophe.Status.CONNECTED) {
alert('connected!');
$(document).trigger('connected');
alert('Connected successfully');
} else if (status === Strophe.Status.DISCONNECTED) {
$(document).trigger('disconnected');
}
else
{
Hello.log("error");
console.log('error');
}
});
Hello.connection = conn;
});
This creates the Strophe.Connection and stores it in Hello.connection. Also, it sets the callback function of the connection object. This code is taken straight from an example in a Strophe.js book. Anyway, the callback checks the status of the connection, and if status === Strophe.Status.DISCONNECTED, triggers "disconnected", which only does this:
$(document).bind('disconnected', function () {
Hello.log("Connection terminated.");
console.log('Connection terminated.');
// remove dead connection object
Hello.connection = null;
});
Anyway, what is happening is that, for some reason, in the callback set with conn.connect, after a short time, the status evaluates to Strophe.Status.DISCONNECTED, and I am not sure why, unless somewhere, either in the server or in the connection object, there is a timeout specified which seems to be ca. 60 seconds.
As to a log of the stanzas going back and forth, I guess I would need to quickly write a handler to see all incoming stanzas, or is it possible to see a log of all stanzas between the client and server in ejabberd?
For the sake of other people who come upon this and have a similar problem, the solution in this case was that the servers at hosted.im send a ping request every 60 seconds to check if the client is still online.
This ping request looks like this:
<iq from="testserver.p1.im" to="chris#testserver.p1.im/23064809721410433741569348" id="164323654" type="get"> <ping xmlns="urn:xmpp:ping"></ping> </iq>
What is needed, of course, is to form a response, which will look something like this:
<iq from="chris#testerver.p1.im" to="testserver.p1.im" id="164323654" type="result" xmlns="jabber:client"><ping xmlns="urn:xmpp:ping"/></iq>
Note the "to"-attribute. I omitted it at the beginning as I was under the assumption a message sent with no to-attribute is automatically assumed to be a client->server message. Not in this case however. Not sure if this is the case in general, or whether it is an oddity of servers at hosted.im.
Thanks to everyone for their comments and suggestions!
Best regards,
Chris

Categories