Search code examples
sslxmppopenfireejabberdxmppframework

XMPP Connection time optimization


We are programming on an iOS Chat Application based on the XMPP Robbie Hanson framework at the moment. Server side we deploy openfire running on 3 servers with hazelcast plugin. Now we encountered following problem: the client connection and authentication takes about 2 sec. without TLS/SSL. With TLS/SSL it takes about 4 sec. We tried everything to shorten this time as it looks strange if the user gets a push notification that he received a message, opens the app and it takes that long to actually get the message. We do not use SRV records so it can’t be the DNS lookup that takes that long. We tried to modify the xmpp handshake so that the user sends all data (startls,auth method...) right from the start without waiting for server response but the server does not accept this. We also tried to use faster servers with very high network bandwith, but this didn't helped. Finally we even tried to use ejabberd but we have exactly the same times so we stayed with openfire.

The reason we thought it MUST be possible to shorten connection times is other messenger like WhatsApp or Threema which need less than 1 sec. So do you have any advice, what else we could try?Is it possible to reach that time only by optimizing the client and without modifying the openfire code?

Thank you so much!

This is my Handshake Log:

C2S - RECV (1083417823): <?xml version='1.0'?>
C2S - RECV (1083417823): <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='chat.example.com'>
C2S - SENT (1083417823): <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="chat.example.com" id="5a051bc8" xml:lang="en" version="1.0">
C2S - SENT (1083417823): <stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features>
C2S - RECV (1083417823): <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
Starting Hazelcast Clustering Plugin
C2S - RECV (1083417823): <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='chat.example.com'>
C2S - SENT (1083417823): <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="chat.example.com" id="5a051bc8" xml:lang="en" version="1.0"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism></mechanisms><auth xmlns="http://jabber.org/features/iq-auth"/></stream:features>
C2S - RECV (1083417823): <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="DIGEST-MD5"/>
C2S - SENT (1083417823): <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09Im5vZGVzLmZsaXhtaW5kZXIuY29tIixub25jer0iQ1hOS3MxWG9WY0xMTmsvedRUWlFIYmpGS1Vta2s4SG5WQ01TWUJnWiIscW9wPSJhdXRoIixjaGFyc2V0PXV0Zi04LGFsZ29yaXRobT1tZDUtc2Vzcw==</challenge>
C2S - RECV (1083417823): <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">dXNlcm5hbWU9IjAwNDkyMjIiLHJlYWxtPSJub2Rlcy5mbGl4bWluZGVyLmNvbSIsbm9uY2UtrkNYTktzMVhvVmNMTE5rL3dEVFpRSGJqRktVbWtrOEhuVkNNU1lCZ1oiLGNub25jZT0iMkU2RURCRTctNUI2NC00QjQwLTg0OUMtQkUzQ0YwMTRCNTk0IixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL25vZGVzLmZsaXhtaW5kZXIuY29tIixyZXNwb25zZT1mMDRhYzM4MjBlY2MwMGE1Mjk1ZTkxMjc5YTc1Zmz4MCxjaGFyc2V0PXV0Zi04</response>
C2S - SENT (1083417823): <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cnNwYXV0aD05NDk2NTA2NWRlNDQ2MzRhNWRlMWNzuTc0NjI3MGNhZg==</success>
C2S - RECV (1083417823): <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' to='chat.example.com'>
C2S - SENT (1083417823): <?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="chat.example.com" id="5a051bc8" xml:lang="en" version="1.0"><stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>
C2S - RECV (1083417823): <iq type="set" id="F3CFA293-6D45-4E03-9065-3FD10D617C02"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/></iq>
C2S - SENT (1083417823): <iq type="result" id="F3CFA293-6D45-4E03-9065-3FD10D617C02" to="chat.example.com/5a051bc8"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>222@chat.example.com/5a051bc8</jid></bind></iq>
C2S - RECV (1083417823): <iq type="set" id="D351FF85-535B-4B08-B5C2-3C11D92C1EA9"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>
C2S - SENT (1083417823): <iq type="result" id="D351FF85-535B-4B08-B5C2-3C11D92C1EA9" to="222@chat.example.com/5a051bc8"/>
C2S - RECV (1083417823): <presence/>
C2S - SENT (1083417823): <presence from="222@chat.example.com/5a051bc8" to="222@chat.example.com/5a051bc8"/>
C2S - SENT (1083417823): <presence from="222@chat.example.com/5a051bc8" to="222@chat.example.com/5a051bc8"/>
CLOSED (1446853640) 

Solution

  • I'd recommend trying to get some logs to find out exactly what is taking the most amount of time. Figuring out exactly how many roundtrips you are using will help you determine what to optimize.

    There's a XEP for XMPP Quickstart, XEP-0305. This has some general recommendation, but also a pipelining protocol which should do the bundling of data for you, if your server and client support it.

    Some tips:

    • Make sure you cache DNS results for the specified TTL.
    • Save the user's roster locally and use roster versioning to only get any changes that might've happend to the user's roster.
    • At the TLS level, you could try to get session resumption or false start working. Also make sure the server sends no extra certificates (like a root you know the client will trust). Use faster algorithms (ECDHE instead of DHE, RSA-2048 instead of RSA-4096), but keep security in mind (please no RC4).
    • If you do stuff like setting/retrieving vCards, service discovery, etc., make sure that happens later and doesn't block anything else.
    • If you are using SCRAM-SHA-1 and the server is using hashed password storage (i.e., sending the same salt every time) you can cache the SaltedPassword value, which should save a large amount of time.
    • If your server is new enough (so implements RFC 6121 correctly), you can skip a roundtrip by skipping the urn:ietf:params:xml:ns:xmpp-session IQ. See https://datatracker.ietf.org/doc/draft-cridland-xmpp-session/?include_text=1.
    • Cache the entity caps of your contacts and your server to skip retrieving them. You can even embed caps that are used often inside your app.
    • If at any step you want to send multiple stanzas at the same time, make sure they are sent in a single TLS packet. Every packet has both an overhead in size (somewhere in between 25-85 extra bytes: header, IV, padding, MAC) and in processing time (parsing, verifying MAC).