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