I recently added, in the iOS app of [AlaMesa][alamesa-ios], the capability of discovering a database update service in the local network, using DNS-SD. It’s quite a mouthful when said out loud, but it’s way simpler than it sounds.
On the phone, I use
NSNetService and it’s family. The system keeps track of published services in the local network and notifies the app about them. On any Mac or Linux desktop, it’s just two steps:
- Place the database file and a small piece of metadata under the document root of any plain HTTP server, be it Apache, Ngnix, lighttpd, Caddy or webfs (even Python’s
http.server, though its performance is quite poor).
dns-sdon Mac or
avahi-publish-serviceon Linux with the proper options.
And that’s basically it.
Now, to let [Yon][yon2x2] use it on Mac as simply as possible, I didn’t want to have to enable Mac’s builtin Apache server, or use Python’s http server module, for the performance problems I mentioned. Nor did I want to be fussing with the command line every time to run
dns-sd. I wanted the whole process to consist on opening and closing a simple app.
I wanted the HTTP sever to be part of the app. I found
qhttp on GitHub and decided to give it a try. It was cool! It used C++14 and Qt, and had a Node.js inspired interface, with a lot of asynchronicity and great performance. And it was a library, so the server would be embedded in the app. But I needed it to handle file transfers, with range requests and all. And it didn’t. I looked briefly for alternatives. I considered qhttpengine, but it still didn’t have what I needed. I found nothing perfect, but qhttp was the coolest contender. So I went back to it and sat down to build the missing parts.
I dusted my RFC 2616 off, and in a night, I added a file transfer capability to my app while keeping it highly asynchronous. Two or three days later, and with the help of Boost’s excellent Interval Containers Library, I implemented range requests. I could use axel or DownThemAll to download from my own http server. I tested with multi-gigabytes files and the performance was above that of webfs, with good parallelism.
On a rush to have it ready for the 2nd Startup Weekend in Havana, I finished the UI, and even put a nice logging system, to gather statistics at the event on how many people would use it. The event got cancelled anyway, but I’ll put it to the test soon enough.
One tricky bump
Yesterday, however, I noticed that iOS was not issuing range requests to my server, while it did for Nginx, Caddy, webfs and Apache. Without range requests, iOS did not resume an interrupted download from where it stopped, it downloaded from the beginning every time.
I spent a good two or three hours Wiresharking all over my network traffic, trying all sorts of configs while comparing the answers of my own server and the ones that did work until I found the problem: I was not sending a
Last-Modified header, and without it, iOS was not convinced that, after an interrupted download, the file was the same, so it resorted to download from scratch. Just adding that header with the right value was enough.
You can check the app and its code here.