Multiple XMPP BOSH Connections Interfering in Internet Explorer - UPDATE: Seen In Other Browsers - xmpp

Introduction
I am working on an MVC web app that is using XMPP for two purposes:
1) Communicating real time data from the server to the client - XMPPMessaging
2) User group chat - XMPPChat
I am using Strophe.js on the client side and MatriX C# library on the server side. XMPPChat is being set up by prebinding on the server side and passing the connection info to the client, which attaches with strophe. XMPPMessaging is not prebinding, just logging in on the client side. Every window that the user opens connects a new XMPPMessaging instance to receive data from the server. Only the chat windows connect XMPPChat and XMPPMessaging. Also, both are connecting to the Punjab connection manager over BOSH, which is forwarding the messages to an Openfire server.
Issue
Currently, I have an issue that only manifests itself in Internet Explorer (I am testing with IE8 and IE9 and both exibit this issue). When I have XMPPMessaging and XMPPChat connected, chat messages are being sent after a very large delay (10 sec - 2 minutes). I've seen this cause disconnection issues for XMPPChat as well. The receive functionality works great; if any other browser posts to the chatroom, IE get's it immediately just like everyone else. When XMPPMessaging is turned off, the chat send runs great with no delays or disconnections. I've stepped through the code with breakpoints and the send functionality is firing immediately. I've also observed the Punjab logs, and it appears that Punjab isn't getting the message till after the delay and is passing them along fine (also evidenced by the other browsers working fine).
I'm mostly wondering if anyone else has seen issues like this one with concurrent BOSH connections in IE comehow interfering.
Update
The chat feature in IE started working today completely inexplicably; I haven't changed the code or any configuration of it at all. I double checked that the messaging connection and chat connection were both running, and they both were connected. In attempts to diagnose the fix, I restarted Apache and the bug came back, again only in IE we saw the chat slow down significantly and randomly disconnect.
Update 2 - SEEN IN OTHER BROWSERS
Today I was able to recreate the issue in other browsers (Chrome, Firefox) by opening multiple chat instances in other tabs. So it seems that IE only suffered worse from the issue - only needing one tab open. This is pointing me towards a browser concurrent connection limit issue, but that doesn't make sense to be either considering that I shouldn't be anywhere near the limit with 2 connections.
Update 3 - CAUSE DETERMINED IN OTHER BROWSERS
I performed a test in Firefox to confirm my suspicions that we are dealing with a concurrent connection issue. I opened one chat tab and the messages posted instantaneously. I opened a second one and, as expected, they delayed. I then opened about:config and changed network.http.max-persistent-connections-per-server from 6 to 7. After doing this I repeated the test. I was able to post messages instantaneously in both the first and second chat tabs, but when I opened a third they all began experiencing the delay. I'm going to try and test in IE to see if this is the same problem by modifying the FEATURE_MAXCONNECTIONSPERSERVER registry settings and seeing if the problem goes away.
Update 4 - IE ISSUE STILL NOT RESOLVED
I performed the same test in IE, changing the registry as shown here, adding iexplorer.exe DWORD settings to the FEATURE_MAXCONNECTIONSPERSERVER and FEATURE_MAXCONNECTIONSPER1_0SERVER registries set to 0xA, but no changes in behavior are apparent. I tried the same thing with the x64 settings (under Wow6432Node) and still observed no changes. I also tried changing the original explorer.exe values from 2 or 4 to 8, but still saw no changes in behavior, all after restarting the computer after each change.
Code
For reference, here is some of my XMPP chat code which may or may not be relevant:
XMPPAttach: function (jid, sid, rid) {
connection = new Strophe.Connection(BOSH_SERVICE);
connection.rawInput = function (data) {
log('RECV: ' + data);
};
connection.rawOutput = function (data) {
log('SENT: ' + data);
};
MY_JID = jid;
connection.addHandler(notifyUser, null, 'message', 'chat', null, null);
connection.addHandler(groupChat, null, 'message', 'groupchat', null, null);
connection.addHandler(presenceInfo, null, 'presence', null, null, null);
connection.ping.addPingHandler(pingHandler);
connection.attach(jid, sid, rid, onConnect, 300);
}
function onConnect(status) {
switch (status) {
case Strophe.Status.CONNECTED:
log('CONNECTED');
break;
case Strophe.Status.ERROR:
log('ERROR');
break;
case Strophe.Status.CONNFAIL:
log('CONNFAIL');
break;
case Strophe.Status.AUTHENTICATING:
log('AUTHENTICATING');
break;
case Strophe.Status.AUTHFAIL:
log('AUTHFAIL');
break;
case Strophe.Status.CONNECTING:
log('CONNECTING');
break;
case Strophe.Status.DISCONNECTED:
log('DISCONNECTED');
break;
case Strophe.Status.DISCONNECTING:
log('DISCONNECTING');
break;
case Strophe.Status.ATTACHED:
log('ATTACHED');
break;
default:
log('UKNOWN STATUS CODE');
break;
}
if ((status == Strophe.Status.CONNECTED || status == Strophe.Status.ATTACHED) && !presenceSent) {
connection.send($pres().tree());
presenceSent = true;
userLogin();
}
else if (status == Strophe.Status.CONNECTING || status == Strophe.Status.AUTHENTICATING) {
// do nothing
}
else if (status == Strophe.Status.AUTHFAIL) {
userInvalidLogin();
}
else {
userLoginFailed();
}
}
EnterChatRoom: function (room, nick) {
ROOM_JID = room;
MY_NICK = nick;
var chatJID = room.concat('/', nick);
var pres = $pres({ to: chatJID }).c('x', { xmlns: 'http://jabber.org/protocol/muc' });
connection.send(pres);
}
SendToRoom: function (text) {
var send = $msg({ to: ROOM_JID, type: 'groupchat' }).c('body', {}, text);
connection.send(send.tree());
},
And here is some of my XMPP messaging code:
function initializeXMPP() {
connection = new Strophe.Connection(BOSH_SERVICE);
intentionalDisconnect = false;
connection.rawInput = function (data) {
log('RECV: ' + data);
};
connection.rawOutput = function (data) {
log('SENT: ' + data);
};
connection.addHandler(onMessage, null, 'message', null, null, null);
connection.ping.addPingHandler(pingHandler);
connection.connect(messageCatcher.serverSettings.clientUser + '#' + messageCatcher.serverSettings.xmppDomain,
messageCatcher.serverSettings.clientUserPassword,
onConnect, 300, undefined, messageCatcher.serverSettings.route);
log('Strophe is connected.');
}
function onConnect(status) {
switch (status) {
case Strophe.Status.ERROR: // 0
log('ERROR');
StandardErrorHandler({ "Message": "XMPP Connection Error", "Status": "ERROR" });
break;
case Strophe.Status.CONNECTING: // 1
log('CONNECTING');
break;
case Strophe.Status.CONNFAIL: // 2
log('CONNFAIL');
StandardErrorHandler({ "Message": "XMPP Connection Error", "Status": "CONNFAIL" });
break;
case Strophe.Status.AUTHENTICATING: // 3
log('AUTHENTICATING');
break;
case Strophe.Status.AUTHFAIL: // 4
log('AUTHFAIL');
StandardErrorHandler("XMPP AUTHFAIL");
break;
case Strophe.Status.CONNECTED: // 5
log('CONNECTED');
callHandlers(connectHandlers);
break;
case Strophe.Status.DISCONNECTED: // 6
log('DISCONNECTED');
onDisconnect();
break;
case Strophe.Status.DISCONNECTING: // 7
log('DISCONNECTING');
break;
case Strophe.Status.ATTACHED: // 8
log('ATTACHED');
break;
default:
log('UKNOWN STATUS CODE');
break;
}
if (status == Strophe.Status.CONNECTED && !presenceSent) {
var pres = $pres({
//type: 'available'
});
connection.send(pres);
presenceSent = true;
log("PRESENCE SENT - SID: " + connection.sid);
}
}

Related

pjsip (pjsua) notification when remote user answers the call

I am trying to make a simple SIP user agent using https://github.com/pjsip/pjproject. I can succesfully connect to a sip server (Twilio) and place calls to PSTN numbers using the pjsua_* interface. This works fine.
What I would like now is to get a notification (through a callback or such) from pjsip when the user that I am calling answers the call.
I am using on_call_state() to get updates on the invite, but this goes through the same states
CALLING -> CONNECTING -> CONFIRMED -> DISCONNCTD
even if the user rejects the call. So I guess I am not looking at the right callback for this.
How can I definitely tell if the user has answered or rejected the call?
for me it is working this way. in on_call_state callback:
pjsua_call_info callInfo;
pjsua_call_get_info(call_id, &callInfo);
pjsip_inv_state state = callInfo.state;
pjsip_status_code statusCode = callInfo.last_status;
switch (state) {
.....
case PJSIP_INV_STATE_CONFIRMED:
// remote party answered the call normally
break;
case PJSIP_INV_STATE_DISCONNECTED:
if (statusCode == PJSIP_SC_BUSY_HERE) {
// rejected
} else {
// finished ok
}
break;
i reject call this way:
pj_status_t state;
int sendCode = PJSIP_SC_DECLINE;
try {
state = pjsua_call_answer((pjsua_call_id) call_id, sendCode, nullptr, nullptr);
} catch (...) {
return -1;
}
return state;

OPC UA Client capture the lost item values from the UA server after a disconnect/connection error?

I am building a OPC UA Client using OPC Foundation SDK. I am able to create a subscription containing some Monitoreditems.
On the OPC UA server these monitored items change value constantly (every second or so).
I want to disconnect the client (simulate a connection broken ), keep the subcription alive and wait for a while. Then I reconnect having my subscriptions back, but I also want all the monitored Item values queued up during the disconnect. Right now I only get the last server value on reconnect.
I am setting a queuesize:
monitoredItem.QueueSize = 100;
To kind of simulate a connection error I have set the "delete subscription" to false on ClosesSession:
m_session.CloseSession(new RequestHeader(), false);
My question is how to capture the content of the queue after a disconnect/connection error???
Should the ‘lost values’ be “new MonitoredItem_Notification” automatically when the client reconnect?
Should the SubscriptionId be the same as before the connection was broken?
Should the sessionId be the same or will a new SessionId let med keep the existing subscriptions? What is the best way to simulate a connection error?
Many questions :-)
A sample from the code where I create the subscription containing some MonitoredItems and the MonitoredItem_Notification event method.
Any OPC UA Guru out there??
if (node.Displayname == "node to monitor")
{
MonitoredItem mon = CreateMonitoredItem((NodeId)node.reference.NodeId, node.Displayname);
m_subscription.AddItem(mon);
m_subscription.ApplyChanges();
}
private MonitoredItem CreateMonitoredItem(NodeId nodeId, string displayName)
{
if (m_subscription == null)
{
m_subscription = new Subscription(m_session.DefaultSubscription);
m_subscription.PublishingEnabled = true;
m_subscription.PublishingInterval = 3000;//1000;
m_subscription.KeepAliveCount = 10;
m_subscription.LifetimeCount = 10;
m_subscription.MaxNotificationsPerPublish = 1000;
m_subscription.Priority = 100;
bool cache = m_subscription.DisableMonitoredItemCache;
m_session.AddSubscription(m_subscription);
m_subscription.Create();
}
// add the new monitored item.
MonitoredItem monitoredItem = new MonitoredItem(m_subscription.DefaultItem);
//Each time a monitored item is sampled, the server evaluates the sample using a filter defined for each monitoreditem.
//The server uses the filter to determine if the sample should be reported. The type of filter is dependent on the type of item.
//DataChangeFilter for Variable, Eventfilter when monitoring Events. etc
//MonitoringFilter f = new MonitoringFilter();
//DataChangeFilter f = new DataChangeFilter();
//f.DeadbandValue
monitoredItem.StartNodeId = nodeId;
monitoredItem.AttributeId = Attributes.Value;
monitoredItem.DisplayName = displayName;
//Disabled, Sampling, (Report (includes sampling))
monitoredItem.MonitoringMode = MonitoringMode.Reporting;
//How often the Client wish to check for new values on the server. Must be 0 if item is an event.
//If a negative number the SamplingInterval is set equal to the PublishingInterval (inherited)
//The Subscriptions KeepAliveCount should always be longer than the SamplingInterval/PublishingInterval
monitoredItem.SamplingInterval = 500;
//Number of samples stored on the server between each reporting
monitoredItem.QueueSize = 100;
monitoredItem.DiscardOldest = true;//Discard oldest values when full
monitoredItem.CacheQueueSize = 100;
monitoredItem.Notification += m_MonitoredItem_Notification;
if (ServiceResult.IsBad(monitoredItem.Status.Error))
{
return null;
}
return monitoredItem;
}
private void MonitoredItem_Notification(MonitoredItem monitoredItem, MonitoredItemNotificationEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new MonitoredItemNotificationEventHandler(MonitoredItem_Notification), monitoredItem, e);
return;
}
try
{
if (m_session == null)
{
return;
}
MonitoredItemNotification notification = e.NotificationValue as MonitoredItemNotification;
if (notification == null)
{
return;
}
string sess = m_session.SessionId.Identifier.ToString();
string s = string.Format(" MonitoredItem: {0}\t Value: {1}\t Status: {2}\t SourceTimeStamp: {3}", monitoredItem.DisplayName, (notification.Value.WrappedValue.ToString().Length == 1) ? notification.Value.WrappedValue.ToString() : notification.Value.WrappedValue.ToString(), notification.Value.StatusCode.ToString(), notification.Value.SourceTimestamp.ToLocalTime().ToString("HH:mm:ss.fff"));
richTextBox1.AppendText(s + "SessionId: " + sess);
}
catch (Exception exception)
{
ClientUtils.HandleException(this.Text, exception);
}
}e here
I don't know how much of this, if any, the SDK you're using does for you, but the approach when reconnecting is generally:
try to resume (re-activate) your old session. If this is successful your subscriptions will already exist and all you need to do is send more PublishRequests. Since you're trying to test by closing the session this probably won't work.
create a new session and then call the TransferSubscription service to transfer the previous subscriptions to your new session. You can then start sending PublishRequests and you'll get the queued notifications.
Again, depending on the stack/SDK/toolkit you're using some or none of this may be handled for you.

Javafx, Determine a specific timeout for reading from socket

In code below, in a thread (not UI thread) I'm trying to read data from a socket. But in each state I want to break the while loop if it took more than 3 seconds. How can I do that?
you can find Some comments in the code
System.out.println(loginMsg);//send login message to server
while ((fromServer = sInput.readLine()) != null) {
switch (state) {
case 0:
if (fromServer.equals("*2*1#")) //Login was successful
{
sOutput.println(msg); // send another message to server to enter some data into database
state = 1;
} else if (fromServer.equals("*2*0#")) //Login was not successful
{
motor1CommandString = "access denied";
state = 3;
}
break;
case 1:
if (fromServer.equals("*6*1#"))
{
motor1CommandString = "message sent";
state = 2;
} else if ((fromServer.equals("*6*2#")) /*device is not online */ || (fromServer.equals("*6*0#"))) /*for some reason device is not reachable */{
motor1CommandString = "No Connection to device";
state = 3;
}
break;
case 2:
if (fromServer.equals(ACK)) //device has received the message and replied back
state = 3;
break;
}
if (state == 3) {
break;
}
}
Call setSoTimeout on the socket (before you try to read from it). Then readLine will throw a SocketTimeoutException if no data is read before the timeout expires. You can catch that exception outside of the loop.

Socket connection gets closed for no apparent reason

I am trying to implement Facebook X_FACEBOOK_PLATFORM SASL mechanism so I could integrate Facebook Chat to my application over XMPP.
Here is the code:
var ak = "my app id";
var sk = "access token";
var aps = "my app secret";
using (var client = new TcpClient())
{
client.Connect("chat.facebook.com", 5222);
using (var writer = new StreamWriter(client.GetStream())) using (var reader = new StreamReader(client.GetStream()))
{
// Write for the first time
writer.Write("<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" to=\"chat.facebook.com\"><auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"X-FACEBOOK-PLATFORM\" /></stream:stream>");
writer.Flush();
Thread.Sleep(500);
// I am pretty sure following works or at least it's not what causes the error
var challenge = Encoding.UTF8.GetString(Convert.FromBase64String(XElement.Parse(reader.ReadToEnd()).Elements().Last().Value)).Split('&').Select(s => s.Split('=')).ToDictionary(s => s[0], s => s[1]);
var response = new SortedDictionary<string, string>() { { "api_key", ak }, { "call_id", DateTime.Now.Ticks.ToString() }, { "method", challenge["method"] }, { "nonce", challenge["nonce"] }, { "session_key", sk }, { "v", "1.0" } };
var responseString1 = string.Format("{0}{1}", string.Join(string.Empty, response.Select(p => string.Format("{0}={1}", p.Key, p.Value)).ToArray()), aps);
byte[] hashedResponse1 = null;
using (var prov = new MD5CryptoServiceProvider()) hashedResponse1 = prov.ComputeHash(Encoding.UTF8.GetBytes(responseString1));
var builder = new StringBuilder();
foreach (var item in hashedResponse1) builder.Append(item.ToString("x2"));
var responseString2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}&sig={1}", string.Join("&", response.Select(p => string.Format("{0}={1}", p.Key, p.Value)).ToArray()), builder.ToString().ToLower()))); ;
// Write for the second time
writer.Write(string.Format("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">{0}</response>", responseString2));
writer.Flush();
Thread.Sleep(500);
MessageBox.Show(reader.ReadToEnd());
}
}
I shortened and shrunk the code as much as possible, because I think my SASL implementation (whether it works or not, I haven't had a chance to test it yet) is not what causes the error.
I get the following exception thrown at my face: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
10053
System.Net.Sockets.SocketError.ConnectionAborted
It happens every time I try to read from client's stream for the second time. As you can see i pause a thread here so Facebook server has enough time to answer me, but I used asynchronous approach before and I encountered the exact same thing, so I decided to try it synchronously first. Anyway actual SASL mechanism implementation really shouldn't cause this because if I don't try to authenticate right away, but I send the request to see what mechanisms server uses and select that mechanism in another round of reading and writing, it fails, but when I send mechanism selection XML right away, it works and fails on whatever second I send.
So the conclusion is following: I open the socket connection, write to it, read from it (first read works both sync and async), write to it for the second time and try to read from it for the second time and here it always fails. Clearly then, problem is with socket connection itself. I tried to use new StreamReader for second read but to no avail. This is rather unpleasant since I would really like to implement facade over NetworkStream with "Received" event or something like Send(string data, Action<string> responseProcessor) to get some comfort working with that stream, and I already had the implementation, but it also failed on second read.
Thanks for your suggestions.
Edit: Here is the code of facade over NetworkStream. Same thing happens when using this asynchronous approach, but couple of hours ago it worked, but for second response returned same string as for first. I can't figute out what I changed in a meantime and how.
public void Send(XElement fragment)
{
if (Sent != null) Sent(this, new XmppEventArgs(fragment));
byte[] buffer = new byte[1024];
AsyncCallback callback = null;
callback = (a) =>
{
var available = NetworkStream.EndRead(a);
if (available > 0)
{
StringBuilder.Append(Encoding.UTF8.GetString(buffer, 0, available));
NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
}
else
{
var args = new XmppEventArgs(XElement.Parse(StringBuilder.ToString()));
if (Received != null) Received(this, args);
StringBuilder = new StringBuilder();
// NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
}
};
NetworkStream.BeginRead(buffer, 0, buffer.Length, callback, buffer);
NetworkStreamWriter.Write(fragment);
NetworkStreamWriter.Flush();
}
The reader.ReadToEnd() call consumes everything until end-of-stream, i.e. until TCP connection is closed.

how to deal with the block of ftp download request

I am downloading file with ftp protocol. Now, to check the ability of dealing with error, I am simulating happening of some network error. The code to handle the network inputStream is as below:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
// An NSStream delegate callback that's called when events happen on our
// network stream.
{
#pragma unused(aStream)
assert(aStream == self.networkStream);
switch (eventCode) {
case NSStreamEventOpenCompleted: {
self.connected = YES;
} break;
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768];
// Pull some data off the network.
bytesRead = [self.networkStream read:buffer maxLength:sizeof(buffer)];
DLog(#"%#,byteRead:%d",self.urlInput,bytesRead);
if (bytesRead == -1) {
[self _stopReceiveWithStatus:#"Network read error"];
} else if (bytesRead == 0) {
[self _stopReceiveWithStatus:#"success"];
} else {
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
bytesWrittenSoFar = 0;
do {
bytesWritten = [self.fileStream write:&buffer[bytesWrittenSoFar] maxLength:bytesRead - bytesWrittenSoFar];
DLog(#"%#,bytesWritten:%d",self.urlInput,bytesWritten);
assert(bytesWritten != 0);
if (bytesWritten == -1) {
[self _stopReceiveWithStatus:#"File write error"];
break;
} else {
bytesWrittenSoFar += bytesWritten;
}
} while (bytesWrittenSoFar != bytesRead);
}
} break;
case NSStreamEventHasSpaceAvailable: {
assert(NO); // should never happen for the output stream
} break;
case NSStreamEventErrorOccurred: {
[self _stopReceiveWithStatus:#"Stream open error"];
} break;
case NSStreamEventEndEncountered: {
assert(NO);
} break;
default: {
assert(NO);
} break;
}
}
If I turn off the wifi manually or turn off my wireless router (Network connection flag is off), a "NSStreamEventErrorOccurred" will be return and the downloading process will be terminated correctly. However, if I turn off the Modem, while keeping the wireless router open (Network connection flag is on). The downloading process stuck at the case "NSStreamEventHasBytesAvailable". Even after I turn on the internet connection , it is still stuck.
I want to know why it is stuck and how can I detect this kind of error. How can I deal with this situation?
First, kudos for considering this and running tests. Many developers assume that "the network connection will always work."
Second, it seems a little odd that you are using NSStream for FTP downloads; you do know that NSURLConnection supports FTP, right? Unless you are doing something really strange you should probably use the built-in URL loading facilities.
In any case, the issue here is that there is in principle no way for an application (or computer) to determine whether there has been a pause in a connection because the connection failed (and should be restarted or canceled) or because it simply is running slowly (in which case patience is required).
I'm a little surprised that an active TCP session is not resumed when your modem is reconnected; that suggests that maybe your ISP's router is dropping the connection when the modem link goes down or that your IP is changing on reconnection, but this isn't important for your question anyway.
TCP sessions will ordinarily eventually get timed out by the OS (or an upstream router) after a period of inactivity, but this might be an unacceptably long time. So the thing you probably need to do is implement a timeout.
What I'd probably do (assuming you stay the course with NSStream as discussed above) is have an NSTimer firing off periodically - maybe every 15 seconds - and have the callback compare the current time to a timestamp that you set on each NSStreamEventHasBytesAvailable event. If the timestamp is too old (say, more than 15 seconds), cancel or restart the download as desired and notify the user.
But again, take a look at just using NSURLConnection.