1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Solutions component.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include <QCoreApplication>
42 #include <QTcpServer>
43 #include <QTcpSocket>
44 #include <QTextStream>
45 #include <QDateTime>
46 #include <QStringList>
47 #include <QDir>
48 #include <QSettings>
49 
50 #include "qtservice.h"
51 
52 // HttpDaemon is the the class that implements the simple HTTP server.
53 class HttpDaemon : public QTcpServer
54 {
55     Q_OBJECT
56 public:
HttpDaemon(quint16 port,QObject * parent=0)57     HttpDaemon(quint16 port, QObject* parent = 0)
58         : QTcpServer(parent), disabled(false)
59     {
60         listen(QHostAddress::Any, port);
61     }
62 
incomingConnection(int socket)63     void incomingConnection(int socket)
64     {
65         if (disabled)
66             return;
67 
68         // When a new client connects, the server constructs a QTcpSocket and all
69         // communication with the client is done over this QTcpSocket. QTcpSocket
70         // works asynchronously, this means that all the communication is done
71         // in the two slots readClient() and discardClient().
72         QTcpSocket* s = new QTcpSocket(this);
73         connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
74         connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
75         s->setSocketDescriptor(socket);
76 
77         QtServiceBase::instance()->logMessage("New Connection");
78     }
79 
pause()80     void pause()
81     {
82         disabled = true;
83     }
84 
resume()85     void resume()
86     {
87         disabled = false;
88     }
89 
90 private slots:
readClient()91     void readClient()
92     {
93         if (disabled)
94             return;
95 
96         // This slot is called when the client sent data to the server. The
97         // server looks if it was a get request and sends a very simple HTML
98         // document back.
99         QTcpSocket* socket = (QTcpSocket*)sender();
100         if (socket->canReadLine()) {
101             QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*"));
102             if (tokens[0] == "GET") {
103                 QTextStream os(socket);
104                 os.setAutoDetectUnicode(true);
105                 os << "HTTP/1.0 200 Ok\r\n"
106                     "Content-Type: text/html; charset=\"utf-8\"\r\n"
107                     "\r\n"
108                     "<h1>Nothing to see here</h1>\n"
109                     << QDateTime::currentDateTime().toString() << "\n";
110                 socket->close();
111 
112                 QtServiceBase::instance()->logMessage("Wrote to client");
113 
114                 if (socket->state() == QTcpSocket::UnconnectedState) {
115                     delete socket;
116                     QtServiceBase::instance()->logMessage("Connection closed");
117                 }
118             }
119         }
120     }
discardClient()121     void discardClient()
122     {
123         QTcpSocket* socket = (QTcpSocket*)sender();
124         socket->deleteLater();
125 
126         QtServiceBase::instance()->logMessage("Connection closed");
127     }
128 
129 private:
130     bool disabled;
131 };
132 
133 class HttpService : public QtService<QCoreApplication>
134 {
135 public:
HttpService(int argc,char ** argv)136     HttpService(int argc, char **argv)
137 	: QtService<QCoreApplication>(argc, argv, "Qt HTTP Daemon")
138     {
139         setServiceDescription("A dummy HTTP service implemented with Qt");
140         setServiceFlags(QtServiceBase::CanBeSuspended);
141     }
142 
143 protected:
start()144     void start()
145     {
146         QCoreApplication *app = application();
147 
148 #if QT_VERSION < 0x040100
149         quint16 port = (app->argc() > 1) ?
150                 QString::fromLocal8Bit(app->argv()[1]).toUShort() : 8080;
151 #else
152         const QStringList arguments = QCoreApplication::arguments();
153         quint16 port = (arguments.size() > 1) ?
154                 arguments.at(1).toUShort() : 8080;
155 #endif
156         daemon = new HttpDaemon(port, app);
157 
158         if (!daemon->isListening()) {
159             logMessage(QString("Failed to bind to port %1").arg(daemon->serverPort()), QtServiceBase::Error);
160             app->quit();
161         }
162     }
163 
pause()164     void pause()
165     {
166 	daemon->pause();
167     }
168 
resume()169     void resume()
170     {
171 	daemon->resume();
172     }
173 
174 private:
175     HttpDaemon *daemon;
176 };
177 
178 #include "main.moc"
179 
main(int argc,char ** argv)180 int main(int argc, char **argv)
181 {
182 #if !defined(Q_OS_WIN)
183     // QtService stores service settings in SystemScope, which normally require root privileges.
184     // To allow testing this example as non-root, we change the directory of the SystemScope settings file.
185     QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope, QDir::tempPath());
186     qWarning("(Example uses dummy settings file: %s/QtSoftware.conf)", QDir::tempPath().toLatin1().constData());
187 #endif
188     HttpService service(argc, argv);
189     return service.exec();
190 }
191