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.