Roster presence when re attaching a connection

1,622 views
Skip to first unread message

hamzakc

unread,
Mar 23, 2012, 4:17:10 AM3/23/12
to Strophe
Hi

I am currently developing a chat feature for a web app. I am using
ejabberd, BOSH and Strophe JS. I have have most of the things
working. Apart from the roster.

When I go to another page or refresh the page I am reattaching to an
existing authenticated BOSH connection (using cookies to store the
last RID). The issue I am having is that the roster is not being
updated when I reattach. The initial presence notifications are not
being sent, as the connection state has not changed (obviously).

What is the best way to get the presence notifications sent again so I
can populate the roster when i navigate to a new page? I am thinking
of using a presence probe for this. i.e. looping through the entire
roster and sending a presence probe for each user. However this does
seem very inefficient. Is there another way of doing it?

I am using the roster plugin in the Professional XMPP Programming
book.

Thanks.

Kind Regards
Hamza Khan-Cheema

piotr_cz

unread,
Mar 26, 2012, 8:27:41 AM3/26/12
to Strophe
I'm trying to find out best way to tackle this.

That's what I came up with so far:
Seems that only way to force users in roster to send the presences is
to change own resource availability to unavailable and then back to
available. This would probably trigger an ugly flicker in other users
contact lists and would be too expensive when there are few continuous
reattachments.


I've added a event that is hooked up to window.onbeforeclose event
that serializes roster and all resources, then stores in a cookie with
lifetime of 'bosh_inactivity'. This is an interval after which
resource will be marked as unavailable by xmpp server (it's sent by
xmpp server during prebind process, usually 30s).
If the client manages to reattach during bosh_inactivity period and
there are any updates to roster, I assume those are sent by server on
new attach.
If the client doesn't manage to reattach, cookie is deleted by the
browser and server markes client resource as unavailable. Client have
to reconnect again and upon reconnection naturally receives other
users presences.

On successful attach I load cookie data and deserialize it. Afterwards
I get rid of it so it's not being sent all over with all other
requests.

This works pretty well. The only downside and a show-stopper is the
browser cookie limit (4kb). Serialized data of 3 users (1 resource per
user) take up around 1kb for my webapp :(


You could use html5 sessionStorage and code own garbage collector, but
you'd have to play with alternatives as this is not available for <IE9
You could try to store roster data on server, but I'm not sure if you
would make it in time to send it during onbeforeclose event.


I hope this helped at least a bit and we will find a solution

piotr_cz

hamzakc

unread,
Mar 26, 2012, 9:58:19 AM3/26/12
to str...@googlegroups.com
Hi.

Thanks for the reply.  I was beginning to think I was the only one with this problem :)

I considered your solution about storing the roster in a cookie, but as you say it is not really the best solution due to the cookie size limitation.  To address the unload not having enough time problem you described, I could send a AJAX request on each presence notification and store the roster in the server session.  I think that is the only bullet proof way of doing it.  Although a bit expensive. 

I am leaning towards sending an unavailable presence then a available presence to the server.  However I am thinking of using the <status> tag to make sure the client roster ignores these presence requests.  That way the roster will not go offline then online for the user.  Can you see any problems with this approach?  I admit it is a bit of a hack, but it seems the most efficient way out of the above options. 

Thanks again.

Kind Regards
Hamza Khan-Cheema

Michael Weibel

unread,
Mar 26, 2012, 10:06:18 AM3/26/12
to str...@googlegroups.com
Hi,

you could also try using the priority-node in your presence stanzas instead of setting type to unavailable. 
I'm currently experimenting with setting, when connecting, priority to e.g. "1" and as soon as the user browses to another page, I set the priority to "-1". 


- Michael

--
You received this message because you are subscribed to the Google Groups "Strophe" group.
To view this discussion on the web visit https://groups.google.com/d/msg/strophe/-/oRUkNNBF6-AJ.
To post to this group, send email to str...@googlegroups.com.
To unsubscribe from this group, send email to strophe+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/strophe?hl=en.

Hamza Khan-Cheema

unread,
Mar 26, 2012, 10:48:43 AM3/26/12
to str...@googlegroups.com
Hi Michael.

Thanks for the suggestion.  I now have it working using the priority setting.  I am sending a priority of -1 when re attaching the connection.  I then modified the roster plugin callback to check it the state is available and the priority is -1, if so it sends a presence stanza to the server and returns.  That way the users roster will be populated.

Thanks again.

Kind Regards
Hamza

Jack Moffitt

unread,
Mar 26, 2012, 11:29:02 AM3/26/12
to str...@googlegroups.com
Reattaching to an existing session is something that is invisible to
the underlying session. The underlying session has no notion of this,
so as far as it is concerned, you've already done your initial
presence and gotten the dump of presence from you contacts.

You need to persist the roster state across the attachment. Reprobing
for presence is pretty expensive.

It's not just the roster state you will need to persist, but all
application state that you need.

jack.

piotr_cz

unread,
Mar 26, 2012, 2:25:36 PM3/26/12
to Strophe
So it seems that there is no best option.

In my case storing data of each user resource in session could lead to
a disaster, as transmitting one presence would result in all users
updating their sessions at the same time. Probably XMPP server is more
robust for such hadling.

You may check out solutions for data persistence at client side using
cross-browser solutions (IE userData may use to 64kb), looks like this
is the preferred way.

I'm working on one-page app so xmpp session reattachements should
happen only in exceptional case, still I'll try to minimize them.

Barnettech

unread,
Apr 13, 2012, 10:19:41 AM4/13/12
to str...@googlegroups.com
After attaching I tried:

Gab.connection.send($pres().c('show').t("away").up().c('status').t("-reconnecting"));

Gab.connection.send($pres());

but it didn't work.  Can I pause the connection and then restart it to update the roster again on reconnecting?  I don't want to save the roster state because if they reconnect after 5-20 minutes the online status of folks on their roster will be out of date, and I don't want to have to reconnect every time.  Although maybe I'll have to.

Barnettech

unread,
Apr 13, 2012, 11:16:53 AM4/13/12
to str...@googlegroups.com
Connecting from the server from php (https://github.com/barnettech/PHP_SPEAKS_XMPP) and then attaching to strophe on each page is actually pretty quick.  I'm thinking this is the way to go.

Jack Moffitt

unread,
Apr 16, 2012, 12:49:57 PM4/16/12
to str...@googlegroups.com
> Gab.connection.send($pres().c('show').t("away").up().c('status').t("-reconnecting"));
>
> Gab.connection.send($pres());

Going away won't work. You have to become unavailable. When you
become available again, this will trigger the server to send out
presence probes to all your contacts, at which point they will answer
with their current presence. If you have a large roster, this can
cause a lot of network traffic. You don't see this traffic on the
client because the server is doing the work for you.

jack.

Matthew Wild

unread,
Apr 16, 2012, 2:50:04 PM4/16/12
to str...@googlegroups.com

To try and present the situation from a high level...

The primary purpose of attaching to an existing session instead of
reconnecting is to avoid the necessary state sync at login (roster,
presence, MUCs, etc.). If you attach, and then want to try to get this
data sent to you again, you are more than likely doing something
wrong. Just make a new connection to the server if you don't have any
of the old state around.

There are exceptions to this rule... some applications don't really
have any state, and so it doesn't matter.

For practically all other applications, you should transfer your local
state (contact list, contact's last presence, and anything else
relevant) to the new client instance, as well as the sid+rid of the
connection. If you are going to start from a blank client, just do
that and log back in - don't try to use the old client's connection.

Regards,
Matthew

Dave Geller

unread,
May 15, 2012, 10:18:44 AM5/15/12
to str...@googlegroups.com
I just went through a struggle with this myself.  I'm using Ejabberd, Ejabberd-Bosh, RubyOnRails(xmpp4r) and Strophe.

Try 1:
Connecting with Strophe from JS on a page.
PROS:
    - Easy.
CONS:
    - Strophe somehow, sporadically kept sending duplicate SID/RID pairs resulting in terminate from Ejabberd and a lost connection.
    - Username / password on page
    - page load performance

Try 2:
Connecting with Ruby/XMMP4R, attaching with Strophe
PROS:
    - No user/pass on page
    - No Javascript connection delay (now it's on the server side)
    - Fewer disconnects from Strophe - for some reason Ejabberd still responds with a terminate as if it received a duplicate RID
      but I don't see the duplicate in the logs so not sure about this
CONS:
    - SID/RID coordination - figured out that since xmpp4r is threaded and Bosh holds stanzas, there's one extra RID increment
      after I go on to generate the page but before Strophe gets involved
    - Presence subscription requests come in to the connecting entity (xmpp4r)... below...

So even though I have xmpp4r sending an "unavailable" presence before moving on and Strophe sending an "available,"  Strophe (as discussed here) doesn't get the subscription presence updates.  The roster and the pubsub messages can be retrieved but not the subscription updates.

Even worse, since Bosh holds stanzas and xmpp4r is necessarily threaded to listen for responses, the subscription presence updates don't come in until after I send the unavailable presence... a few seconds after... which means that I'm generating the Strophe page with an empty list of subscription requests.

The solution to this was a really ugly, smelly delay that I had to put into the ruby code that waits for the stanza to come in and be processed.

Missing from the XEP's that would really help:
Connecting without getting presence updates (could be a Bosh feature) Maybe a new type of status?

Jack Moffitt

unread,
May 15, 2012, 12:44:07 PM5/15/12
to str...@googlegroups.com
>     - Presence subscription requests come in to the connecting entity
> (xmpp4r)... below...

You can avoid this by not sending available presence until you've
attached with strophe. Until the first presence is sent, you are
considered unavailable.

> Missing from the XEP's that would really help:
> Connecting without getting presence updates (could be a Bosh feature) Maybe
> a new type of status?

This is not needed per the above.

jack.

Dave Geller

unread,
May 15, 2012, 2:48:13 PM5/15/12
to str...@googlegroups.com
Thanks for the reply and above all, thanks for your book... not sure where I'd be without that jumpstart.


You can avoid this by not sending available presence until you've
attached with strophe. Until the first presence is sent, you are
considered unavailable.

Well, I don't send presence:available, but when sending presence:unavailable (which is how xmpp4r implements close),
the server would send back the presence updates... delayed at that.

If I don't send the "unavailable" then xmpp4r often still has the connection open when I generate the page and Strophe fails because the server responds with a stream conflict and terminates the connection.  Also, the RID would increment unpredictably by 1 or 2 depending on how fast Ejabberd was responding.  Maybe I'm missing something, I've been fighting this for over a week.

What finally worked is sending this and not sending the unavailable presence (per XEP-0124 Section 10):
<body rid='109042' sid='e58d9e922645b1d2ba4487e29f346562414a113f' xmlns='http://jabber.org/protocol/httpbind' pause='120'/>

Incidentally, it also works if I do send the unavailable presence as long as the presence also has the pause attribute (discovered through a coding error).

120 is the value of maxpause, received from the server.

So then it was just a matter, once again, of updating my branch of xmpp4r to allow arbitrary attributes and an empty body. :-)  The other thing I had to do was to for xmpp4r is to create a "connection_only" method that doesn't start a keepalive thread.

Matthew Wild

unread,
May 15, 2012, 3:02:05 PM5/15/12
to str...@googlegroups.com
On 15 May 2012 19:48, Dave Geller <ma...@davidgeller.net> wrote:
> Thanks for the reply and above all, thanks for your book... not sure where
> I'd be without that jumpstart.
>
>
>> You can avoid this by not sending available presence until you've
>> attached with strophe. Until the first presence is sent, you are
>> considered unavailable.
>
>
> Well, I don't send presence:available, but when sending presence:unavailable
> (which is how xmpp4r implements close),
> the server would send back the presence updates... delayed at that.

It shouldn't, that would be a bug in your server. Presence updates
(including subscription requests) are only sent to "available"
clients. Sending an unavailable presence, quite obviously, does not
make the client available.

> If I don't send the "unavailable" then xmpp4r often still has the connection
> open when I generate the page and Strophe fails because the server responds
> with a stream conflict and terminates the connection.

When a stream is properly closed, the server should implicitly make
you unavailable. If it doesn't, that is again a bug in your server.

Regards,
Matthew

Daniel Dormont

unread,
May 15, 2012, 3:02:58 PM5/15/12
to str...@googlegroups.com
I myself don't actually use an XMPP connection at all on the server side for this particular purpose, only a simple HTTP request to the "warm" service provided by Theo Cushion's mod_warm_bindings for Ejabberd. The key is that once the BOSH connection is "established" on the server side, you need your application server _not_ to send the initial presence to Ejabberd. Instead, just retrieve the SID and RID, and let Strophe send the initial presence after successful connection attachment on the client side.

I'm not a Ruby programmer, but from the looks of it xmpp4r expects that you'll be maintaining a persistent connection from your application to the XMPP server.

-Dan

--
You received this message because you are subscribed to the Google Groups "Strophe" group.
To view this discussion on the web visit https://groups.google.com/d/msg/strophe/-/fH9GAYv-wo8J.

Dave Geller

unread,
May 15, 2012, 3:35:06 PM5/15/12
to str...@googlegroups.com
Yes, xmppr4 expects that you want a persistent connection.  There's not much else available on the Ruby front.  I've been making changes to it as necessary.  It's hard to believe that it hasn't been touched in years, doesn't have a maintainer and yet is the basis for just about every xmpp thing out there in Ruby.

So, yes, at the end of the day, I don't know why ejabberd (or the ejabberd implementation of Bosh) isn't doing the right thing.

At this point, I don't care anymore.  I have it working well enough.  When I have time, I'll take a look at that warm_bindings module; seems valuable.  Thank you.
Reply all
Reply to author
Forward
0 new messages