conversejs / converse.js

Web-based XMPP/Jabber chat client written in JavaScript
http://conversejs.org
Mozilla Public License 2.0
3.08k stars 772 forks source link

Issue with prebind #65

Closed coderhs closed 11 years ago

coderhs commented 11 years ago

Hi there,

I am integrating conversejs to my rails application. I am facing a peculiar issue, for which i couldn't find the reason so far. When i directly log in, through the form provided. Everything works fine, i get to the see whos online and even what chat that person, and also to other converjs clients in my website.

When i do the same with prebind. I can't see any one online, everyone will be marked as offline. But when i try to chat with the use who is shown as offline, but is actually online through my desktop client, the messages does reach. And I am able to reply as well. But when i try to chat with other users, who are online via converjs, its not working.

My jabber server is prosody, i am using the backed library called xmpp4r, the session id, rid all looks fine.

Converse configuration

    require(['converse'], function (converse) {
        $.getJSON('/prebind', function (data) {
            converse.initialize({
                auto_list_rooms: false, 
                auto_subscribe: false,
                bosh_service_url: 'http://example.com/http-bind', 
                hide_muc_server: false,
                i18n: locales.en, 
                prebind: true,
                jid: data.jid,
                sid: data.sid,
                rid: data.rid,
                show_controlbox_by_default: false,
                xhr_user_search: false
            });
        });
    });

My ruby backend code

  def prebind
    require 'xmpp4r/httpbinding/client'
    @client = Jabber::HTTPBinding::Client.new(current_user.jabber_id)
    @client.connect("http://example.com/http-bind")
    @client.auth(current_user.jabber_password)

    respond_to do |format|
      msg = { :jid => @client.instance_variable_get("@jid").inspect, :sid => @client.instance_variable_get("@http_sid"),
             :rid => @client.instance_variable_get("@http_rid"), :bosh_service_url => 'http://example.com/http-bind' }
      format.json  { render :json => msg }
    end
  end
jcbrand commented 11 years ago

You have prebind set to false in your converse.initialize call.

coderhs commented 11 years ago

oh sorry for pasting the wrong, config. I was checking if the issue is there when i directly log in. I tried it with prebind = true. Let me just correct that.

wlma commented 11 years ago

I`m facing same error:

js:

require(['converse'], function (converse) { 
$.getJSON('http://domain.example/prebind.php', function (data) {

    converse.initialize({
        prebind: true,
        debug: true,
        bosh_service_url: 'https://domain.example/http-bind',
        jid: data.jid,
        sid: data.sid,
        rid: data.rid,
        auto_list_rooms: true,
        auto_subscribe: false,
        hide_muc_server: false,
        i18n: locales.en, // Refer to ./locale/locales.js to see which locales are supported
        show_controlbox_by_default: true,
        xhr_user_search: false
    });
}); 
});

Server-side:

header('Cache-Control: no-cache, must-revalidate');
header('Content-type: application/json; charset=utf-8');
header("Access-Control-Allow-Origin: http://static.wuapaa.com");

require ('jaxl/jaxl.php');
$client = new JAXL(array(
    'jid'=>'jon.doe@domain.example',
    'pass'=>'foobar',
    'bosh_url' => 'https://domain.example/http-bind'
));

$client ->add_cb('on_auth_success', function(){
    global $client;

    $array = array(
        'rid' => $client->xeps['0206']->rid,
        'sid' => (string)$client->xeps['0206']->sid,
        'jid'=> $client->jid->to_string(),
        'bosh_url' => 'https://domain.example/http-bind'
    );

    echo json_encode($array); 
    exit;
});
$client->add_cb('on_auth_failure', function($reason) {
global $client;
    echo("got on_auth_failure cb with reason $reason");
$client->send_end_stream();
});
$client->start();
echo "done";
wlma commented 11 years ago

Ok, so it seems I fixed my issue by setting a status "available" within the server-side code. Maybe this is the solution for coderhs also?

coderhs commented 11 years ago

Oh! where did you give that, status as available? after authenticating or gave it along with the authentication.

wlma commented 11 years ago

as i use jaxs I did it within the on_auth_success function (which is executed after authentication):

$client ->add_cb('on_auth_success', function(){
    global $client;
    $client->set_status("available!"); //fix offline after prebind
    $array = array(
        'rid' => $client->xeps['0206']->rid,
        'sid' => (string)$client->xeps['0206']->sid,
        'jid'=> $client->full_jid->to_string(),
        'bosh_url' => 'https://domain.example/http-bind'
    );

    echo json_encode($array); 
    exit;
});
jcbrand commented 11 years ago

As far as I know you shouldn't have to set the status server side. It can actually cause problems because other users and clients might start responding to you before converse.js is properly initialized and set up.

converse.js sends a presence as soon as your roster is set up (once converse.js is ready to handle incoming presence stanzas).

See https://github.com/jcbrand/converse.js/blob/master/converse.js#L2735

If you don't have roster contacts, then the presence is still sent:

See https://github.com/jcbrand/converse.js/blob/master/converse.js#L2676

I suggest you put breakpoints at these two places and see whether the presence stanza is being sent out for you.

rubytastic commented 11 years ago

@codehs you where able to fix your code to work? I tried your example but it fails with HTTP request path is empty

There is also bosh4r gem but its outdated Im currently try to make that implementation work, also on Prosody here

jcbrand commented 11 years ago

@coderhs: I think I've found your problem and the solution.

When the roster is fetched from localStorage, users are added very quickly and your presence stanza is sent out before the presence handler has been registered.

Once you have sent out your own presence stanza, you will start receiving presence stanzas from your buddies, but before converse.js is able to process them. That's why all your roster contacts are shown as offline.

I've submitted a fix for this (see above).

I'd appreciate it if you could let me know whether this fixes your problem.

coderhs commented 11 years ago

@jcbrand It didn't work, still the same issue. I just noticed, that when i changed the status in the converse.js it is being changed. in the client as well, but unless I am the one who initiated the chat, i won't get the chat messages from the desktip client, if he tries to send anything.

Could it be an issue with the backend, but the sid and rid if get seems to be fine an example of the values i get

{jid: "hsps@example.com/9d685bc4-f5db-40a6-97b0-633945e2fc4e", sid: "02a73b56-0c93-472e-b9fd-517aad460a43", rid: 43462} 

{jid: "hsps@example.com", sid: "02a73b56-0c93-472e-b9fd-517aad460a43", rid: 43462}
coderhs commented 11 years ago

@rubytastic I used the xmpp4r library, present available version of the gem that is available from rubygems won't work. Actually most xmpp/jabber library for ruby are not working. A couple of us have started to fix up the old xmpp4r library, we are planning to launch the new version of xmpp4r gem. You can use the code at http://github.com/xmpp4r/xmpp4r

jcbrand commented 11 years ago

@coderhs What desktop client are you using? If your desktop client is sending messages to a specific connected resource which is not your converse.js client, then converse.js won't receive those messages.

http://wiki.xmpp.org/web/Jabber_Resources

coderhs commented 11 years ago

I am using Empathy Instant Messenger, true, i guess that could be the reason. Though no issues exist if i directly log into conversejs with the username and password.

Could the issue, be related to session id?

jcbrand commented 11 years ago

On 10/04/2013 01:58 PM, Harisankar P S wrote:

I am using Empathy Instant Messenger, true, i guess that could be the reason. Though no issues exist if i directly log into conversejs with the username and password. Could it be maybe an issue with the session id?

Yeah, it's probably not that then.

If it's really only with prebind, then I would guess it's your SID or RID values. But the fact that you can send messages seems to invalidate that hypothesis.

The best thing you can do is set debug=true and check all the output to the browser console. Your clue is probably in there somewhere.

coderhs commented 11 years ago

Pasting the output of the console on prebind.

Attached converse.min.js:138
0 _throttledRequestHandler called with 0 requests converse.min.js:138
0 _throttledRequestHandler called with 0 requests converse.min.js:138
0 _throttledRequestHandler called with 0 requests converse.min.js:138
0 _throttledRequestHandler called with 0 requests converse.min.js:138

0 request id 1.0 posting converse.min.js:138
0 request id 1.0 state changed to 1 converse.min.js:138
<body rid=​"175398" xmlns=​"http:​/​/​jabber.org/​protocol/​httpbind" sid=​"d481a72f-5028-4fb2-bab8-b49415127cf3">​
<iq from=​"hsps@example.com/​79185a45-64ee-4814-b7b5-0391131795ed" to=​"example.com" type=​"get" xmlns=​"jabber:​client" id=​"2998:​sendIQ">​…​</iq>​
<iq from=​"hsps@example.com/​79185a45-64ee-4814-b7b5-0391131795ed" to=​"example.com" type=​"get" xmlns=​"jabber:​client" id=​"2999:​sendIQ">​…​</iq>​
<presence xmlns=​"jabber:​client">​…​</presence>​
<iq type=​"get" id=​"3000:​roster" xmlns=​"jabber:​client">​
<query xmlns=​"jabber:​iq:​roster">​</query>​
</iq>​
</body>​

Correct me if i am wrong, but looks like the converse js did query for a roster, but didn't get any reply. Where as when i do the same by direct login, it gives the full roster.

jcbrand commented 11 years ago

The XML you pasted there shows that converse.js requested the user's roster.

Specifically this part:

<iq type=​"get" id=​"3000:​roster" xmlns=​"jabber:​client">​
<query xmlns=​"jabber:​iq:​roster">​</query>​
</iq>​

You should then in another BOSH request receive the roster from the server.

Here is an example what it should look like (taken from here: http://xmpp.org/rfcs/rfc3921.html#roster)

<iq to='juliet@example.com/balcony' type='result' id='roster_1'>
  <query xmlns='jabber:iq:roster'>
    <item jid='romeo@example.net'
          name='Romeo'
          subscription='both'>
      <group>Friends</group>
    </item>
    <item jid='mercutio@example.org'
          name='Mercutio'
          subscription='from'>
      <group>Friends</group>
    </item>
    <item jid='benvolio@example.org'
          name='Benvolio'
          subscription='both'>
      <group>Friends</group>
    </item>
  </query>
</iq>

Have you double-checked that you don't get a reply like the above?

If so, I would check the prosody logs. I've never used prosody myself, so I don't know what you'll find, but it might say that it is receiving unauthorised requests (for example if your SID or RID are wrong).

jcbrand commented 11 years ago

On 10/04/2013 11:33 AM, Harisankar P S wrote:

Could it be an issue with the backend, but the sid and rid if get seems to be fine an example of the values i get

|{jid: "hsps@example.com/9d685bc4-f5db-40a6-97b0-633945e2fc4e", sid: "02a73b56-0c93-472e-b9fd-517aad460a43", rid: 43462}

{jid: "hsps@example.com", sid: "02a73b56-0c93-472e-b9fd-517aad460a43", rid: 43462}

Are these two different requests? Why do you have a JID resource in the one example and not in the other?

Also, why is the RID the same in both? The RID is supposed to change between requests.

coderhs commented 11 years ago

@jcbrand I just meant, I tried in both those format, one with the resource my backend xmpp request returned, and another with out the resource. Both failed sorry about the confusion.

I will look at prosody, and raise a support request there. I did find something mysterious such that it seems, it is destroying the previous session when converse.js logs in using the same session.

jcbrand commented 11 years ago

I'll close this ticket then, since it seems this is related to your prebinding on prosody.

rubytastic commented 11 years ago

@coderhs Would you mind to place a message here if you find out why this happens? Kind regards

AMilkov commented 11 years ago

@coderhs Is there any solution to this problem?

I am facing exactly the same situation with Prosody, prebind and converse.js. Every time when I do connection.attach with old session parameters the session is lost.

Interesting thing is that the check:

if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED))

Is actually passing.

rubytastic commented 11 years ago

Also still same issue, haven't found a solution yet either. I suggest someone goes to the prosody web chat, you can chat directly with prosody creators there. https://prosody.im/chat/ and please post info here also, Im unable to at this time. Let's help each other fix this frustrating issue!

rubytastic commented 11 years ago

Updates on this?

dazed19 commented 11 years ago

This is just a thought - I'm tuning in late - but I have prebind working into Converse.js with eJabberd and SMACK library (I know might be well different).

But do you need to explicitly close the "client" connection into XMPP server on the app server after the connection (the one you bind on) before giving the jid, sid, rid to Converse? Without ripping down the XMPP session.

My setup didn't work until I added this - and I had to craft a different close mechanism to avoid publishing offline presence etc.

If the XMPP server sees two BOSH clients connected for the same SID (one from the App server and one from the Converse client) then where does it send the presence response? Might be worth packet capturing on your XMPP server to see if a response to the bind is sent and what IP is sends it to.

ziglee commented 10 years ago

Maybe you've forgot to increment the RID after the pre-bind and before passing it to the attach function?

babu3009 commented 10 years ago

@coderhs did you fix this issue if so then how ?

babu3009 commented 10 years ago

i am facing similar issue, i am able to login correctly but untill i manually click on status and set it on-line my presence is not sent. also the system sometimes do not load rosters. i tried to debug and call both presence and roster from code but it never executes and the console does not give any error even though i have enabled the debug option.

please post the solution here.

AMilkov commented 10 years ago

Guys there is no way to reuse the old sessions JID & SID unless you are able to supply the next RID, which is the old RID incremented with one.

So instead of remembering the old RID, issuing close command and then reusing the old session just open brand new session. Very simple and is working in 100% of the cases for me. This is how I solved the BOSH + prebind config with Prosody server.

babu3009 commented 10 years ago

@AMilkov how did you do that? i mean on which line i need to close and open a new session ?

babu3009 commented 10 years ago

i fixed by modifying following line this.connection.attach(this.jid, this.sid, this.rid, this.onConnect); to this.connection.attach(this.jid, this.sid, parseInt(this.rid,10)+1, this.onConnect);