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
Related
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.
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.
I am building an app with friends relations using Parse Cloud Code.
When Alice sends Bob a friend request, it is a notification of type 71.
When Bob answers he sends a notification of type 8.
On the server, when a type 8 notification is sent, first some friendship relations are proceeded. They are: removing from both users from each other "potential friend list" and adding to "friend list" instead.
Afterwards, the notification of type 71 should be changed to a type 1 notification.
For some reasons I've been struggling for 24h to make it work. I simply cant proceed those two functions one after the other: the second one is never executed. Here is my code
Parse.Cloud.afterSave("Notification", function(request, response) {
Parse.Cloud.useMasterKey();
var downQuery = new Parse.Query("Notification");
var namnamA = request.object.get('nameA');
var namnamB = request.object.get('nameB');
var tytype = request.object.get('type');
var alice = Parse.User.current();
if (tytype === 8){
var bobQuery = new Parse.Query(Parse.User);
bobQuery.equalTo("username", namnamB);
bobQuery.first().then(function(bob) {
var alicesRelation = alice.relation("friendsList");
var alicesPotRelation = alice.relation("potFriendsList");
var bobsRelation = bob.relation("friendsList");
var bobsPotRelation = bob.relation("potFriendsList");
alicesPotRelation.remove(bob);
alicesRelation.add(bob);
bobsPotRelation.remove(alice);
bobsRelation.add(alice);
return Parse.Object.saveAll([alice, bob]);
}).then(function() {
downQuery.equalTo('nameA', namnamB);
downQuery.equalTo('nameB', namnamA);
downQuery.equalTo('type', 71);
return downQuery.find();
}).then(function(notizz) {
notizz.set('type', 1);
return Parse.Object.saveAll([notizz]);
}).then(function() {
console.log("success " + arguments);
response.success(arguments);
}), function(error) {
console.log("error " + error.message);
response.error(error);
}
}
});
Any help would greatly improve the life expectancy of my computer. Thank you.
aftersave does not have a response, only a request.
https://parse.com/docs/js/guide
Therefore, you do not need to (and should not) be using a response.
Further, when you call return, it exits the function
I created code like this for getting news from xml export from another website and I am trying to fill with it my database.
function UpdateLunchTime() {
var httpRequest = require('request');
var xml2js = require('xml2js');
var parser = new xml2js.Parser();
var url = 'http://www...com/export/xml/actualities';
httpRequest.get({
url: url
}, function(err, response, body) {
if (err) {
console.warn(statusCodes.INTERNAL_SERVER_ERROR,
'Some problem.');
} else if (response.statusCode !== 200) {
console.warn(statusCodes.BAD_REQUEST,
'Another problem');
} else {
//console.log(body);
parser.parseString(body, function (err2, result) {
//console.log(result.Root.event);
var count = 0;
for (var i=0;i<result.Root.event.length;i++)
{
//console.log(result.Root.event[i]);
InsertActionToDatabase(result.Root.event[i]);
}
/*
result.Root.event.forEach(function(entry) {
InsertActionToDatabase(entry);
});
*/
});
}
});
}
function InsertActionToDatabase(action)
{
var queryString = "INSERT INTO Action (title, description, ...) VALUES (?, ?, ...)";
mssql.query(queryString, [action.akce[0], action.description[0],...], {
success: function(insertResults) {
},
error: function(err) {
console.log("Problem: " + err);
}
});
}
For individual actualities it's working fine but when I run it over whole xml I get this error:
Error: [Microsoft][SQL Server Native Client 10.0][SQL Server]Resource ID : 1. The request limit for the database is 180 and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
And for a few last objects I get this error:
Error: [Microsoft][SQL Server Native Client 10.0]TCP Provider: Only one usage of each socket address (protocol/network address/port) is normally permitted.
Thanks for help
The problem is that you're trying to make too many concurrent (insert) operations in your database. Remember that in node.js (almost) everything is asynchronous, so when you call InsertActionToDatabase for one of the items, this operation will start right away and not wait before it finishes to return. So you're basically trying to insert all of the events at once, and as the error message said there's a limit on the number of concurrent connections which can be made to the SQL server.
What you need to do is to change your loop to run asynchronously, by waiting for one of the operations to complete before starting the next one (you can also "batch" send a smaller number of operations at once, continuing after each batch is complete, but the code is a little more complicated) as shown below.
var count = result.Root.event.length;
var insertAction = function(index) {
if (index >= count) return;
InsertActionToDatabase(result.Root.event[i], function() {
insertAction(index + 1);
});
}
insertAction(0);
And the InsertActionToDatabase function would take a callback parameter to be called when it's done.
function InsertActionToDatabase(item, done) {
var table = tables.getTable('event');
table.insert(item, {
success: function() {
console.log('Inserted event: ', item);
done();
}
});
}
In the getCursor_ function below, please explain how I could determine if IndexedDb has opened and if not re-run function once it has? The getCursor_ runs correctly. However, since all of these calls are asynchronous, the function fails when executing before the database has completed opening.
This code is executed in a separate process:
var ixDb;
var ixDbRequest;
ixDbRequest = window.indexedDB.open(dbName, dbVersion);
ixDbRequest.onsuccess = function (e) {
ixDb = ixDbRequest.result || e.result;
};
The getCursor_ function below works fine unless ixDbRequest has not completed execution. I figured out how to test for that, but I am not sure how to wait in the instance where the open database request is still running.
function getCursor_(objectStoreName) {
if (!ixDb) {
if (ixDbRequest) {
// "Database is still opening. Need code to wait or re-run
// once completed here.
// I tried the following with no luck:
ixDbRequest.addEventListener ("success",
getCursor_(objectStoreName), false);
return;
}
}
var transaction = ixDb.transaction(objectStoreName,
IDBTransaction.READ_ONLY);
var objectStore = transaction.objectStore(objectStoreName);
try{
var request = objectStore.openCursor();
return request;
}
catch(e){
console.log('IndexedDB Error' + '(' + e.code + '): ' + e.message);
}
}
UPDATE BELOW:
The answer from #Dangerz definitely helped put me on the right track. However, since the function call is asynchronous, I also ended up having to add a callback in order to actually be able to use the cursor once the "success" event finally fired, and I was able to get the requested IndexedDb cursor. The final working function is below (re-factored slightly to remove the negative logic above "if(!ixDb)" . I am totally open to suggestions, if anyone sees room for improvement!
//****************************************************************************
//Function getCursor - Returns a cursor for the requested ObjectStore
// objectStoreName - Name of the Object Store / Table "MyOsName"
// callback - Name of the function to call and pass the cursor
// request back to upon completion.
// Ex. getCursor_("ObjectStore_Name", MyCallBackFunction);
// Function MyCallBackFunction(CursorRequestObj) {
// CursorRequestObj.onsuccess =
// function() {//do stuff here};
// }
//****************************************************************************
function getCursor_(objectStoreName, callback) {
//The the openCursor call is asynchronous, so we must check to ensure a
// database connection has been established and then provide the cursor
// via callback.
if (ixDb) {
var transaction =
ixDb.transaction(objectStoreName, IDBTransaction.READ_ONLY);
var objectStore = transaction.objectStore(objectStoreName);
try{
var request = objectStore.openCursor();
callback(request);
console.log('ixDbEz: Getting cursor request for '
+ objectStoreName + ".");
}
catch(e){
console.log('ixDbEz Error' + ' getCursor:('
+ e.code + '): ' + e.message);
}
}
else {
if (ixDbRequest) {
ixDbRequest.addEventListener ("success"
, function() { getCursor_(objectStoreName, callback); }
, false);
}
}
}
Change your addEventListener line to:
ixDbRequest.addEventListener ("success", function() { getCursor_(objectStoreName) }, false);