XMPP sucks

    We had a social application without chat, 2 weeks to develop it and absolutely no knowledge about the existing protocols for implementing IM. Not that it would be the necessary kit in order to shoot yourself in the foot, but in the process it happened. Repeatedly.

    - Pasha, we need to make a chat.
    - Yes, everything is simple, my friends here used XMPP for chat in their application.

    What were our requirements? Nothing special, simple messaging between users, without group conversations. Platforms: web (with support for working through web sockets), Android, iOS. Creating users should automatically be done only by our server application. Of course, it would be nice to have notes about whether the message was read or not (it is assumed that the application can be used from different devices), and be able to view the chat log. In general, standard functionality for instant messaging in 2015. Bonus points are awarded if the server can scale horizontally.

    Hmm, that sounds tempting. Google comparison with other standards, open a link to an article on Wikipedia . First thought: wow, cool, there is everything we need.

    Why did we choose XMPP?
    • XMPP is an open protocol developed by XSF (XMPP Standards Foundation), an independent non-profit organization. These guys should be pretty smart and have everything they need for the messaging protocol (yeah, of course).
    • Since the protocol is open, there must be quite a few server implementations, and at least one of them will be good enough to use it.
    • For the same reason, there will probably be 1-2 libraries for each of the programming languages.
    • The first letter X in the abbreviation means extensible, extensible. Even if we don’t like something in the protocol, we can always expand it with our own methods.


    Protocol


    The guys who developed the protocol really provided a lot. For example, take a look at the design of message archiving . Individual archiving settings for sessions. Description of what to do if one of the users wants his messages not to be stored on the server, and the second wants this. At the same time, there is no normal mechanism for receiving an archive of messages by pages. By normal, I mean setting offset and limit. Here you can set only the max parameter, which sets the maximum number of messages and the start parameter, which means the time from which you will receive messages.

    Or, for example: the protocol does not determine what should be done with offline messages (messages sent to the user offline). Therefore, most servers will simply send them to the user at the next login. Remember the requirement that the user can use the application from multiple devices? So, if you run the application on a smartphone, and then enter the web application, then all offline messages will come to your smartphone and ... that's it. That is, you can’t find out about this in a web application. It is worth noting that some servers behave differently, that is, they implement XEP-0013 .

    Oh yes, the protocol for IM (instant messaging) in 2015 does not have a specification that would allow you to get a list of unread messages and mark some of them as read. Absolutely.

    Implementations


    At the very beginning, I did not know what problems from the previous paragraph I would encounter and therefore, MongooseIM was chosen as the server implementation. Scalable, able to prohibit registration only from certain ip, supports websockets. The JSJaC library was chosen for the web front-end, because it provided a more convenient API compared to strophe.js. A simple page from JSJaC examples connected to the server with a half-kick and my joy knew no bounds. It would seem that the work is finished. Almost.

    And then I ran into the last voiced problem from the previous paragraph. Since the protocol does not imply the ability to receive unread messages, and does not at all mean that the messages are read / unread, this part of the functionality will have to be appended to yourself. Being not good at erlang programming, I set about looking for other suitable server implementations. I was looking for implementations in Java or JavaScript.

    Openfire is one of the most popular implementations of XMPP in Java. The pluses include the ease of setup: everything happens through the web interface. Then there are only cons: demanding resources, lack of a plug-in for working through web sockets.

    The only thing worth mentioning from JavaScript projects was xmpp-ftw. The project implements many extensions from XMPP. However, we had to abandon its use, since we would not be able to use existing client libraries. Perhaps everything would not be so good with him.

    Tigase at first seemed like a salvation. Fast, scalable server in Java, with the ability to write a plugin and quite simply plug it in. And lack of documentation. Instead, it was recommended that you read the source code. But that’s nothing, I’m doing it most often. I wrote a plugin that marked new messages unread. In order to register users, I had to write them directly to the database, since the administration API could not be used normally. Fortunately, Tigase has a driver for connecting to Mongodb. Receiving unread messages was done using a separate method of the application’s API (a crutch, it could be made a plug-in inside Tigase, but it would have taken a lot more time, because a fairly low-level API is used to communicate with the DBMS inside Tigase). Having connected everything, I checked - until the message is marked as read (by the way, messages do not have an id in xmpp, so it was decided to mark as read all correspondence with a specific user), it is returned to the unread list. Everything works. It remains to check only the case with an offline message. Yes, it was not marked unread, because the plugin that processes offline messages fires earlier than archiving. At the Tigase forum, the developer replied that the behavior I needed was implemented in the commercial Tigase Unified Archive project. Googling by its name did not lead to anything, the developer on the forum said that the project is still unstable and there is no release version. Rummaging through the source of the server I found that you can get the right behavior by setting all messages to chat. therefore, it was decided to mark read all correspondence with a specific user), it is returned in the list of unread. Everything works. It remains to check only the case with an offline message. Yes, it was not marked unread, because the plugin that processes offline messages fires earlier than archiving. At the Tigase forum, the developer replied that the behavior I needed was implemented in the commercial Tigase Unified Archive project. Googling by its name did not lead to anything, the developer on the forum said that the project is still unstable and there is no release version. Rummaging through the source of the server I found that you can get the right behavior by setting all messages to chat. therefore, it was decided to mark read all correspondence with a specific user), it is returned in the list of unread. Everything works. It remains to check only the case with an offline message. Yes, it was not marked unread, because the plugin that processes offline messages fires earlier than archiving. At the Tigase forum, the developer replied that the behavior I needed was implemented in the commercial Tigase Unified Archive project. Googling by its name did not lead to anything, the developer on the forum said that the project is still unstable and there is no release version. Rummaging through the source of the server I found that you can get the right behavior by setting all messages to chat. it was not marked unread, because the plugin that processes offline messages fires earlier than archiving. At the Tigase forum, the developer replied that the behavior I needed was implemented in the commercial Tigase Unified Archive project. Googling by its name did not lead to anything, the developer on the forum said that the project is still unstable and there is no release version. Rummaging through the source of the server I found that you can get the right behavior by setting all messages to chat. it was not marked unread, because the plugin that processes offline messages fires earlier than archiving. At the Tigase forum, the developer replied that the behavior I needed was implemented in the commercial Tigase Unified Archive project. Googling by its name did not lead to anything, the developer on the forum said that the project is still unstable and there is no release version. Rummaging through the source of the server I found that you can get the right behavior by setting all messages to chat.

    Now let's go through the customer implementations. Let's start with JavaScript implementations. We tried only JSJaC, which eventually changed to strophe.js. The first has a more pleasant API, but it has only basic functionality, not supporting work with extensions, for example, with the archive. At the same time, strophe instead offers a fairly convenient xml builder. Well, abstract from the internals of XMPP did not work. By the way, the web socket connector in JSJaC only works in Webkit.

    Of the Java clients, only Smack has been tried at the moment. If you send an invalid packet, a NoResponseException will most likely be thrown, and that’s all you will know.

    Conclusion


    Maybe my problem was that I was hoping that someone would solve some of my problems for me and did it well. If I knew all this first, I would suggest writing my own chat server. Seriously. It's not that the protocol is bad or not suitable for use to implement IM with modern requirements, although I still think so. I just would have preferred to have my own code and lay the necessary opportunities for expansion than to shovel so many other people's code and specifications. XMPP is quite successfully used to solve a huge range of tasks for which it, presumably, is well suited. Although most solutions use only basic functionality.

    Also popular now: