File indexing completed on 2025-01-26 04:24:52

0001 /*
0002  * Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
0003  * 
0004  * Permission is hereby granted, free of charge, to any person obtaining a copy
0005  * of this software and associated documentation files (the "Software"), to
0006  * deal in the Software without restriction, including without limitation the
0007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
0008  * sell copies of the Software, and to permit persons to whom the Software is
0009  * furnished to do so, subject to the following conditions:
0010  * 
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  * 
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0019  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
0020  * IN THE SOFTWARE. 
0021  */
0022 
0023 #ifndef Q_HTTP_SERVER
0024 #define Q_HTTP_SERVER
0025 
0026 #define QHTTPSERVER_VERSION_MAJOR 0
0027 #define QHTTPSERVER_VERSION_MINOR 1
0028 #define QHTTPSERVER_VERSION_PATCH 0
0029 
0030 #include <QObject>
0031 #include <QHostAddress>
0032 
0033 class QTcpServer;
0034 
0035 class QHttpRequest;
0036 class QHttpResponse;
0037 
0038 /*!
0039  * A map of request or response headers
0040  */
0041 typedef QHash<QString, QString> HeaderHash;
0042 
0043 /*!
0044  * Maps status codes to string reason phrases
0045  */
0046 extern QHash<int, QString> STATUS_CODES;
0047 
0048 /*! \mainpage %QHttpServer Documentation
0049  *
0050  * \section introduction Introduction
0051  *
0052  * %QHttpServer is a easy to use, fast and light-weight
0053  * HTTP Server suitable for C++ web applications backed
0054  * by Qt. Since C++ web applications are pretty uncommon
0055  * the market for this project is pretty low.
0056  *
0057  * But integrating this with a module like QtScript
0058  * and using it to write JavaScript web applications is
0059  * a tempting possibility, and something that I want to
0060  * demonstrate at <a href="http://conf.kde.in">conf.kde.in 2011</a>.
0061  *
0062  * %QHttpServer uses a signal-Q_SLOTS based mechanism
0063  * for all communication, so no inheritance is required.
0064  * It tries to be as asynchronous as possible, to the
0065  * extent that request body data is also delivered as and
0066  * when it is received over the socket via Q. This
0067  * kind of programming may take some getting used to.
0068  *
0069  * %QHttpServer is backed by <a href="http://github.com/ry/http-parser">Ryan
0070  * Dahl's secure and fast http parser</a> which makes it streaming
0071  * till the lowest level.
0072  *
0073  * \section usage Usage
0074  *
0075  * Using %QHttpServer is very simple. Simply create a QHttpServer,
0076  * connect a slot to the newRequest() signal and use the request and
0077  * response objects.
0078  * See the QHttpServer class documentation for an example.
0079  *
0080  * \example helloworld/helloworld.cpp
0081  * \example helloworld/helloworld.h
0082  * \example greeting/greeting.cpp
0083  * \example greeting/greeting.h
0084  * \example bodydata/bodydata.cpp
0085  * \example bodydata/bodydata.h
0086  */
0087 
0088 /*! \class QHttpServer
0089  * The QHttpServer class forms the basis of the %QHttpServer
0090  * project. It is a fast, non-blocking HTTP server.
0091  *
0092  * These are the steps to create a server and respond to requests.
0093  *
0094  * <ol>
0095  * <li>Create an instance of QHttpServer.</li>
0096  * <li>Connect a slot to the newRequest(QHttpRequest*, QHttpResponse*)
0097  * signal.</li>
0098  * <li>Create a QCoreApplication to drive the server event loop.</li>
0099  * <li>Respond to clients by writing out to the QHttpResponse object.</li>
0100  * </ol>
0101  *
0102  * helloworld.cpp
0103  * \include helloworld/helloworld.cpp
0104  * helloworld.h
0105  * \include helloworld/helloworld.h
0106  *
0107  */
0108 class QHttpServer : public QObject
0109 {
0110     Q_OBJECT
0111 
0112 public:
0113     /*!
0114      * Create a new HTTP Server
0115      */
0116     QHttpServer(QObject *parent = 0);
0117     virtual ~QHttpServer();
0118 
0119     /*!
0120      * Start the server bound to the @c address and @c port.
0121      * This function returns immediately!
0122      *
0123      * \param address Address on which to listen to. Default is to listen on
0124      * all interfaces which means the server can be accessed from anywhere.
0125      * \param port Port number on which the server should run.
0126      * \return true if the server was started successfully, false otherwise.
0127      * \sa listen(quint16), listen(const QString&, quint16)
0128      */
0129     bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port=0);
0130 
0131     /*!
0132      * Start the server bound to the @c address and @c port.
0133      * This function returns immediately!
0134      *
0135      * \param address Address on which to listen to.
0136      * \param port Port number on which the server should run.
0137      * \return true if the server was started successfully, false otherwise.
0138      * \sa listen(quint16), listen(const QHostAddress&, quint16)
0139      */
0140     Q_INVOKABLE bool listen(const QString &address, quint16 port=0);
0141 
0142     /*!
0143      * Starts the server on @c port listening on all interfaces.
0144      *
0145      * \param port Port number on which the server should run.
0146      * \return true if the server was started successfully, false otherwise.
0147      * \sa listen(const QHostAddress&, quint16), listen(const QString&, quint16)
0148      */
0149     Q_INVOKABLE bool listen(quint16 port);
0150 
0151     /*!
0152      * Stop listening for connections
0153      */
0154     void close();
0155 
0156 Q:
0157     /*!
0158      * This signal is emitted whenever a client
0159      * makes a new request to the server.
0160      *
0161      * The slot should use the @c request and @c response
0162      * objects to communicate with the client.
0163      *
0164      * \section memorymanagement Memory Management
0165      *
0166      * The QHttpRequest and QHttpResponse deletion policies
0167      * are such.
0168      *
0169      * QHttpRequest is <strong>never</strong> deleted by %QHttpServer.
0170      * Since it is not possible to determine till what point the application
0171      * may want access to its data, it is up to the application to delete it.
0172      * A recommended way to handle this is to create a new responder object for
0173      * every request and to delete the request in that object's destructor. The
0174      * object itself can be deleted by connecting to QHttpResponse's done()
0175      * slot as explained below.
0176      *
0177      * You should <strong>NOT</strong> delete the QHttpRequest object until it
0178      * has emitted an QHttpRequest::end() signal.
0179      *
0180      * QHttpResponse queues itself up for auto-deletion once the application
0181      * calls its end() method. Once the data has been flushed to the underlying
0182      * socket, the object will Q_EMIT a QHttpResponse::done() signal before queueing itself up
0183      * for deletion. You should <strong>NOT</strong> interact with the response
0184      * object once it has emitted QHttpResponse::done() although actual deletion does not
0185      * happen until QHttpResponse::destroyed() is emitted.
0186      * QHttpResponse::done() serves as a useful way to handle memory management of the
0187      * application itself. For example:
0188      *
0189      * \code
0190      * MyApp::MyApp()
0191      *     : QObject(0)
0192      * {
0193      *   QHttpServer *s = new QHttpServer;
0194      *   connect(s, SIGNAL(newRequest(...)), this, SLOT(handle(...)));
0195      *   s.listen(8000);
0196      * }
0197      *
0198      * void MyApp::handle(QHttpRequest *request, QHttpResponse *response)
0199      * {
0200      *   if( request->url() matches a route )
0201      *     new Responder(request, response);
0202      *   else
0203      *     new PageNotFound(request, response);
0204      * }
0205      *
0206      * ...
0207      *
0208      * Responder::Responder(QHttpRequest *request, QHttpResponse *response)
0209      * {
0210      *   m_request = request;
0211      *
0212      *   connect(request, SIGNAL(end()), response, SLOT(end()));
0213      *   // Once the request is complete, the response is ended.
0214      *   // when the response ends, it deletes itself
0215      *   // the Responder object connects to done()
0216      *   // which will lead to it being deleted
0217      *   // and this will delete the request.
0218      *   // So all 3 are properly deleted.
0219      *   connect(response, SIGNAL(done()), this, SLOT(deleteLater()));
0220      *   response->writeHead(200);
0221      *   response->write("Quitting soon");
0222      * }
0223      *
0224      * Responder::~Responder()
0225      * {
0226      *   delete m_request;
0227      *   m_request = 0;
0228      * }
0229      * \endcode
0230      *
0231      */
0232     void newRequest(QHttpRequest *request, QHttpResponse *response);
0233 
0234 private Q_SLOTS:
0235     void newConnection();
0236 
0237 private:
0238     QTcpServer *m_tcpServer;
0239 };
0240 
0241 #endif