1 /*
2 SPDX-FileCopyrightText: 2010-2016 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
3 SPDX-FileContributor: David Faure <david.faure@kdab.com>
4
5 This file initially comes from the KD Soap library.
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
8 */
9
10 #ifndef HTTPSERVER_P_H
11 #define HTTPSERVER_P_H
12
13 #include <QMutex>
14 #include <QSemaphore>
15 #include <QSslError>
16 #include <QTcpServer>
17 #include <QTcpSocket>
18 #include <QThread>
19
20 class BlockingHttpServer;
21
22 class HttpServerThread : public QThread
23 {
24 Q_OBJECT
25 public:
26 enum Feature {
27 Public = 0, // HTTP with no ssl and no authentication needed
28 Ssl = 1, // HTTPS
29 BasicAuth = 2, // Requires authentication
30 Error404 = 4, // Return "404 not found"
31 // bitfield, next item is 8
32 };
Q_DECLARE_FLAGS(Features,Feature)33 Q_DECLARE_FLAGS(Features, Feature)
34
35 HttpServerThread(const QByteArray &dataToSend, Features features)
36 : m_dataToSend(dataToSend)
37 , m_features(features)
38 {
39 start();
40 m_ready.acquire();
41 }
~HttpServerThread()42 ~HttpServerThread() override
43 {
44 finish();
45 wait();
46 }
47
setContentType(const QByteArray & mime)48 void setContentType(const QByteArray &mime)
49 {
50 QMutexLocker lock(&m_mutex);
51 m_contentType = mime;
52 }
53
setResponseData(const QByteArray & data)54 void setResponseData(const QByteArray &data)
55 {
56 QMutexLocker lock(&m_mutex);
57 m_dataToSend = data;
58 }
59
setFeatures(Features features)60 void setFeatures(Features features)
61 {
62 QMutexLocker lock(&m_mutex);
63 m_features = features;
64 }
65
66 void disableSsl();
serverPort()67 inline int serverPort() const
68 {
69 QMutexLocker lock(&m_mutex);
70 return m_port;
71 }
endPoint()72 QString endPoint() const
73 {
74 return QString::fromLatin1("%1://127.0.0.1:%2/path").arg(QString::fromLatin1((m_features & Ssl) ? "https" : "http")).arg(serverPort());
75 }
76
77 void finish();
78
receivedData()79 QByteArray receivedData() const
80 {
81 QMutexLocker lock(&m_mutex);
82 return m_receivedData;
83 }
receivedHeaders()84 QByteArray receivedHeaders() const
85 {
86 QMutexLocker lock(&m_mutex);
87 return m_receivedHeaders;
88 }
resetReceivedBuffers()89 void resetReceivedBuffers()
90 {
91 QMutexLocker lock(&m_mutex);
92 m_receivedData.clear();
93 m_receivedHeaders.clear();
94 }
95
header(const QByteArray & value)96 QByteArray header(const QByteArray &value) const
97 {
98 QMutexLocker lock(&m_mutex);
99 return m_headers.value(value);
100 }
101
102 protected:
103 /* \reimp */ void run() override;
104
105 private:
106 QByteArray makeHttpResponse(const QByteArray &responseData) const;
107
108 private:
109 QByteArray m_partialRequest;
110 QSemaphore m_ready;
111 QByteArray m_dataToSend;
112 QByteArray m_contentType;
113
114 mutable QMutex m_mutex; // protects the 4 vars below
115 QByteArray m_receivedData;
116 QByteArray m_receivedHeaders;
117 QMap<QByteArray, QByteArray> m_headers;
118 int m_port;
119
120 Features m_features;
121 BlockingHttpServer *m_server;
122 };
123
Q_DECLARE_OPERATORS_FOR_FLAGS(HttpServerThread::Features)124 Q_DECLARE_OPERATORS_FOR_FLAGS(HttpServerThread::Features)
125
126 // A blocking http server (must be used in a thread) which supports SSL.
127 class BlockingHttpServer : public QTcpServer
128 {
129 Q_OBJECT
130 public:
131 BlockingHttpServer(bool ssl)
132 : doSsl(ssl)
133 , sslSocket(nullptr)
134 {
135 }
136 ~BlockingHttpServer() override
137 {
138 }
139
140 QTcpSocket *waitForNextConnectionSocket()
141 {
142 if (!waitForNewConnection(20000)) { // 2000 would be enough, except in valgrind
143 return nullptr;
144 }
145 if (doSsl) {
146 Q_ASSERT(sslSocket);
147 return sslSocket;
148 } else {
149 // qDebug() << "returning nextPendingConnection";
150 return nextPendingConnection();
151 }
152 }
153
154 void incomingConnection(qintptr socketDescriptor) override;
155
156 void disableSsl()
157 {
158 doSsl = false;
159 }
160
161 private Q_SLOTS:
162 void slotSslErrors(const QList<QSslError> &errors)
163 {
164 qDebug() << "server-side: slotSslErrors" << sslSocket->errorString() << errors;
165 }
166
167 private:
168 bool doSsl;
169 QTcpSocket *sslSocket;
170 };
171
172 #endif /* HTTPSERVER_P_H */
173