Sunday, February 06, 2011

QHttpServer: Web Apps in Qt

Qt is a great GUI toolkit, but it is also an entire C++ standard library waiting to be used for other tasks. In addition the network module is really powerful. I’ve been playing around with node.js for a while now, and realized that Qt’s default asynchronous nature maps over perfectly to create a event-based web server. To make things better, Ryan Dahl’s small and fast http-parser is freely available. So I just combined the two, and here is QHttpServer.



Here is a ‘web-application’ written completely in C++/Qt. You can even try it out.



#!cpp
#include "greeting.h"

#include <QCoreApplication>
#include <QRegExp>
#include <QStringList>

#include <qhttpserver.h>
#include <qhttprequest.h>
#include <qhttpresponse.h>

Greeting::Greeting()
{
QHttpServer *server = new QHttpServer;
server->listen(QHostAddress::Any, 5000);
connect(server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
this, SLOT(handle(QHttpRequest*, QHttpResponse*)));
}

void Greeting::handle(QHttpRequest *req, QHttpResponse *resp)
{
QRegExp exp("^/user/([a-z]+)$");
if( exp.indexIn(req->path()) != -1 )
{
resp->setHeader("Content-Type", "text/html");
resp->writeHead(200);
QString name = exp.capturedTexts()[1];

QString reply = tr("<html><head><title>Greeting App</title></head><body><h1>Hello %1!</h1></body></html>");
resp->end(reply.arg(name).toAscii());
}
else
{
resp->writeHead(403);
resp->end("You aren't allowed here!");
}
}

int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);

Greeting hello;

app.exec();
}


Awesome isn’t it? You launch an instance of QHttpServer, it emits a signal whenever a new request comes in, you can handle and respond to it. The code is fully documented, so you can do a git clone and run doxygen in the docs/ folder to get API documentation. For now it doesn’t deal with everything that can happen in HTTP, but it does know about keep-alives (no chunked encoding though). QHttpServer is streaming, see the body data example.



Here is a simple ApacheBench run comparing qhttpserver and node.



QHttpServer Greeting app:



> ab -n 1000 -c 100 http://localhost:5000/user/nikhil
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)


Server Software:
Server Hostname: localhost
Server Port: 5000

Document Path: /user/nikhil
Document Length: 88 bytes

Concurrency Level: 100
Time taken for tests: 0.467 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 151000 bytes
HTML transferred: 88000 bytes
Requests per second: 2142.47 [#/sec] (mean)
Time per request: 46.675 [ms] (mean)
Time per request: 0.467 [ms] (mean, across all concurrent requests)
Transfer rate: 315.93 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 4.7 0 23
Processing: 14 43 18.9 40 123
Waiting: 13 43 18.9 40 123
Total: 14 45 18.9 41 130

Percentage of the requests served within a certain time (ms)
50% 41
66% 48
75% 53
80% 58
90% 69
95% 80
98% 98
99% 117
100% 130 (longest request)


node.js greeting app:



> ab -n 1000 -c 100 http://localhost:5000/user/nikhil
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)


Server Software:
Server Hostname: localhost
Server Port: 5000

Document Path: /user/nikhil
Document Length: 88 bytes

Concurrency Level: 100
Time taken for tests: 0.441 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 151000 bytes
HTML transferred: 88000 bytes
Requests per second: 2267.94 [#/sec] (mean)
Time per request: 44.093 [ms] (mean)
Time per request: 0.441 [ms] (mean, across all concurrent requests)
Transfer rate: 334.43 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 4.4 0 18
Processing: 4 40 22.7 37 101
Waiting: 4 40 22.9 37 101
Total: 4 42 21.7 39 101

Percentage of the requests served within a certain time (ms)
50% 39
66% 50
75% 57
80% 63
90% 73
95% 83
98% 91
99% 95
100% 101 (longest request)


While not a very scientific benchmark, this does show them pretty close. C++ is compiled, but I believe node has less ‘layers’ and a particularly efficient I/O infrastructure which allows it to be better even with an interpreted language.



I would really like to see this being used in Qt apps that need a web interface for remote control, or for simple delivery of files.



In addition, if you will be attending conf.kde.in in Bangalore, India on March 9th-13th, my talk on Qt Scripting will involve writing a thin JavaScript wrapper over this and doing some other cool stuff! So be there.

23 comments:

  1. Or you could use Wt from http://www.webtoolkit.eu/wt

    ReplyDelete
  2. It would be cool to have something like Wt in Qt. Perhaps using QML or translating standard widgets to HTML.

    ReplyDelete
  3. I don't understand why you are using QTcpSocket when there is QNetworkAccessManager and the others to already provide this functionality?

    ReplyDelete
  4. @harsh:
    Wt is a full fledged web application development platform, I just wanted to implement QHttpServer as a nice hack (since this isn't what Qt is actually made for) and to use in some other things.

    @comawhite: QNetworkAccessManager is only for clients to make requests. the http server is backed by a QTcpServer which gives me a QTcpSocket every time a client connects.

    ReplyDelete
  5. Awesome stuff, and really useful too! Your basic API is really good, and clear to a point where others can build their own stuff on top of. I could really use it for a UPnP or SOAP server.

    Or someone could port a Django-like API to Qt.. :p *ducks*

    I do find it pretty impressive that node.js is able to process faster then a native C++ application. The real difference off course, happens with real life applcations.

    ReplyDelete
  6. @Diederik: Thanks for the compliments.

    Of course for UPnP you might want to check out HUPnP (http://herqq.org) which may already do whatever you need.

    ReplyDelete
  7. Qt can't replace the STL…

    @Victor
    There has been a proof of concept for translating Qt Widgets to HTML+JavaScript.

    ReplyDelete
  8. Nice post nikhil! I find it nice to see some interest in a Qt based http server.

    I also spent the last few weeks writing my own Qt Http server too (code is at https://github.com/acossette/pillow). My inspiration also came from Node.js and my several years of Ruby web development experience that showed me that writing web applications and web frameworks can be fun and simple!

    Our API is quite similar!

    ReplyDelete
  9. @Jonathon
    STL isn't that great anyways and butt ugly.

    @nikhil
    Ah okay. Is it possible to make it similar like QNetworkAccessManager/Request/Response etc?

    ReplyDelete
  10. @comawhite
    It would be possible to provide an interface similar to QNetworkRequest/Reply but not use them.

    Because a NetworkRequest 'sends' data from a client's point of view, while on the server, this same request is actually 'received' data which is immutable. Similarly a response is 'sent' by the server and 'received' by the client.

    ReplyDelete
  11. But some containers do not have appropriate Qt alternatives and Qt ist far away from providing an ultimate container and algorithm library.

    ReplyDelete
  12. its really great posting..
    thanks for sharing..web development

    ReplyDelete
  13. Having a Qt/HTTP server combination is probably extremely useful. However, one would want more advanced HTTP features, e.g. keep alive, pipelining, too. What is the status for this?

    Probably another useful path to go is to combine a non-Qt webserver framework and just combine that with Qt. mongrel2 could be a starting point for that, but there are quite a few out there.

    ReplyDelete
  14. Hello! Do you have any plan to support WebSockets?

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Node.JS uses the V8 javascript "interpreter" from Google.
    V8 isn't really an "interpreter" because it first translate to NATIVE code of the machine and then it execute the code (using jit compilation).
    So, Node.JS served better because of having less layers and running natively.

    I think that if you recompile QT Framework tunned to your processor family with optimization level of -O3 (and qthttpserver too), you will beat Node.JS.

    ReplyDelete
  17. What is going to happen in a more complex website?

    Are we going to have the same results if we add: user authentication, database connection, file transferring, and other stuff?

    Other thin is that you are using a translatable string in this line

    QString reply = tr("something...");

    Doesn't that means that you are adding an unnecessary layer?


    ReplyDelete
  18. Nice post.Give it up. Thanks for share this article. For more visit:Web App Development

    ReplyDelete
  19. How to fix 4 errors like \moc_qhttpresponse.cpp:130: error: C2491: 'QHttpResponse::staticMetaObject' : definition of dllimport static data member not allowed

    ReplyDelete
  20. This comment has been removed by the author.

    ReplyDelete
  21. I think qhttpserver is great!

    It's not trying to be a replacement for apache or nginx... it's a basic interpreter for http protocol that allows a simple, possibly multi-threaded interface for QT-based applications. Much easier than writing a dbus or tcpsocket implementation for IPC, and doesn't force http requests to refer to actual files.

    Maybe in some future version there will be advanced things like authentication and cookies, but if you're using this as an IPC layer to an application, why use http at all for fancy stuff like that?

    Also... IMHO, shaving a few milliseconds off one library or another doesn't really compare to saving weeks or months of development time when implementing (cloud-based) systems that need to talk to each other. Most languages have at least some form of http client implementation, and this library fills a gap in QT which is an http server implementation.

    ReplyDelete
  22. Oh, and I forgot to say... thanks for writing this!

    ReplyDelete