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