File indexing completed on 2024-12-08 09:40:32

0001 /*
0002     SPDX-FileCopyrightText: 2010-2016 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: David Faure <david.faure@kdab.com>
0004 
0005     This file initially comes from the KD Soap library.
0006 
0007     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
0008 */
0009 
0010 #ifndef HTTPSERVER_P_H
0011 #define HTTPSERVER_P_H
0012 
0013 #include <QMutex>
0014 #include <QSemaphore>
0015 #include <QSslError>
0016 #include <QTcpServer>
0017 #include <QTcpSocket>
0018 #include <QThread>
0019 
0020 class BlockingHttpServer;
0021 
0022 class HttpServerThread : public QThread
0023 {
0024     Q_OBJECT
0025 public:
0026     enum Feature {
0027         Public = 0, // HTTP with no ssl and no authentication needed
0028         Ssl = 1, // HTTPS
0029         BasicAuth = 2, // Requires authentication
0030         Error404 = 4, // Return "404 not found"
0031                       // bitfield, next item is 8
0032     };
0033     Q_DECLARE_FLAGS(Features, Feature)
0034 
0035     HttpServerThread(const QByteArray &dataToSend, Features features)
0036         : m_dataToSend(dataToSend)
0037         , m_features(features)
0038     {
0039         start();
0040         m_ready.acquire();
0041     }
0042     ~HttpServerThread() override
0043     {
0044         finish();
0045         wait();
0046     }
0047 
0048     void setContentType(const QByteArray &mime)
0049     {
0050         QMutexLocker lock(&m_mutex);
0051         m_contentType = mime;
0052     }
0053 
0054     void setResponseData(const QByteArray &data)
0055     {
0056         QMutexLocker lock(&m_mutex);
0057         m_dataToSend = data;
0058     }
0059 
0060     void setFeatures(Features features)
0061     {
0062         QMutexLocker lock(&m_mutex);
0063         m_features = features;
0064     }
0065 
0066     void disableSsl();
0067     inline int serverPort() const
0068     {
0069         QMutexLocker lock(&m_mutex);
0070         return m_port;
0071     }
0072     QString endPoint() const
0073     {
0074         return QString::fromLatin1("%1://127.0.0.1:%2/path").arg(QString::fromLatin1((m_features & Ssl) ? "https" : "http")).arg(serverPort());
0075     }
0076 
0077     void finish();
0078 
0079     QByteArray receivedData() const
0080     {
0081         QMutexLocker lock(&m_mutex);
0082         return m_receivedData;
0083     }
0084     QByteArray receivedHeaders() const
0085     {
0086         QMutexLocker lock(&m_mutex);
0087         return m_receivedHeaders;
0088     }
0089     void resetReceivedBuffers()
0090     {
0091         QMutexLocker lock(&m_mutex);
0092         m_receivedData.clear();
0093         m_receivedHeaders.clear();
0094     }
0095 
0096     QByteArray header(const QByteArray &value) const
0097     {
0098         QMutexLocker lock(&m_mutex);
0099         return m_headers.value(value);
0100     }
0101 
0102 protected:
0103     /* \reimp */ void run() override;
0104 
0105 private:
0106     QByteArray makeHttpResponse(const QByteArray &responseData) const;
0107 
0108 private:
0109     QByteArray m_partialRequest;
0110     QSemaphore m_ready;
0111     QByteArray m_dataToSend;
0112     QByteArray m_contentType;
0113 
0114     mutable QMutex m_mutex; // protects the 4 vars below
0115     QByteArray m_receivedData;
0116     QByteArray m_receivedHeaders;
0117     QMap<QByteArray, QByteArray> m_headers;
0118     int m_port;
0119 
0120     Features m_features;
0121     BlockingHttpServer *m_server;
0122 };
0123 
0124 Q_DECLARE_OPERATORS_FOR_FLAGS(HttpServerThread::Features)
0125 
0126 // A blocking http server (must be used in a thread) which supports SSL.
0127 class BlockingHttpServer : public QTcpServer
0128 {
0129     Q_OBJECT
0130 public:
0131     BlockingHttpServer(bool ssl)
0132         : doSsl(ssl)
0133         , sslSocket(nullptr)
0134     {
0135     }
0136     ~BlockingHttpServer() override
0137     {
0138     }
0139 
0140     QTcpSocket *waitForNextConnectionSocket()
0141     {
0142         if (!waitForNewConnection(20000)) { // 2000 would be enough, except in valgrind
0143             return nullptr;
0144         }
0145         if (doSsl) {
0146             Q_ASSERT(sslSocket);
0147             return sslSocket;
0148         } else {
0149             // qDebug() << "returning nextPendingConnection";
0150             return nextPendingConnection();
0151         }
0152     }
0153 
0154     void incomingConnection(qintptr socketDescriptor) override;
0155 
0156     void disableSsl()
0157     {
0158         doSsl = false;
0159     }
0160 
0161 private Q_SLOTS:
0162     void slotSslErrors(const QList<QSslError> &errors)
0163     {
0164         qDebug() << "server-side: slotSslErrors" << sslSocket->errorString() << errors;
0165     }
0166 
0167 private:
0168     bool doSsl;
0169     QTcpSocket *sslSocket;
0170 };
0171 
0172 #endif /* HTTPSERVER_P_H */