diff options
Diffstat (limited to 'tradeshow/knx-demo/3d-Alexa-knx-demo/qminimalhttpserver.cpp')
-rw-r--r-- | tradeshow/knx-demo/3d-Alexa-knx-demo/qminimalhttpserver.cpp | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/tradeshow/knx-demo/3d-Alexa-knx-demo/qminimalhttpserver.cpp b/tradeshow/knx-demo/3d-Alexa-knx-demo/qminimalhttpserver.cpp new file mode 100644 index 0000000..bef2161 --- /dev/null +++ b/tradeshow/knx-demo/3d-Alexa-knx-demo/qminimalhttpserver.cpp @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qminimalhttpserver.h" + +QMinimalHttpServer::QMinimalHttpServer(QObject *parent) + : QObject(parent) +{ + connect(&tcpServer, &QTcpServer::newConnection, this, &QMinimalHttpServer::onNewTcpConnection); +} + +QMinimalHttpServer::~QMinimalHttpServer() +{ +} + +void QMinimalHttpServer::addFile(QString filename, QByteArray &data, QString mimetype) +{ + filename = filename.toUpper(); // ensure case insensitivity + if (!filename.startsWith("/")) + filename = QStringLiteral("/") + filename; + + httpFiles[filename] = data; + if (mimetype == "") { + QMimeDatabase db; + QList<QMimeType> types = db.mimeTypesForFileName(filename); + if (types.size() > 0) + mimetype = types[0].name(); + } + httpFileMimeTypes[filename] = mimetype; +} + +void QMinimalHttpServer::onNewTcpConnection() +{ + for (QTcpSocket* socket = tcpServer.nextPendingConnection(); socket; socket = tcpServer.nextPendingConnection()) { + #ifdef QT_DEBUG + qDebug() << "QMinimalHttpServer::connected " << socket->peerAddress().toString() << ":" << socket->peerPort(); + #endif + + connect(socket, &QTcpSocket::readyRead, [this, socket](){ + this->onTcpDataReceived(socket); + }); + connect(socket, &QTcpSocket::disconnected, [this, socket](){ + #ifdef QT_DEBUG + qDebug() << "QMinimalHttpServer::disconnected " << socket->peerAddress().toString() << ":" << socket->peerPort(); + #endif + //emit this->disconnected(socket); + }); + } +} + +void QMinimalHttpServer::onTcpDataReceived(QTcpSocket *socket) +{ + QByteArray data = socket->readAll(); + #ifdef QT_DEBUG + qDebug() << "QMinimalHttpServer::onTcpDataReceived " << data; + #endif + this->processTcpData(data, socket); +} + +void QMinimalHttpServer::processTcpData(const QByteArray &data, QTcpSocket *socket) +{ + QStringList lines = QString::fromUtf8(data).split("\r\n"); + QStringList request = lines[0].split(" ", QString::SkipEmptyParts); + QString responseHead = QStringLiteral("HTTP/1.1 200 OK\r\n"); + QByteArray responseBody; + int status = 200; + + QString filename = request[1].toUpper(); + if (!filename.startsWith("/")) + filename = QStringLiteral("/") + filename; + filename = filename.replace("%20", " "); + + if (request[0].toUpper() == "GET") { + if (httpFiles.contains(filename)) { + responseBody = httpFiles[filename]; + responseHead += QStringLiteral("content-type: ") + httpFileMimeTypes[filename] + "\r\n"; + } else if (this->processHttpGetRequest(filename, responseHead, responseBody)) { + // in this case, everything must be done inside processGetRequest + } else { + responseHead = "HTTP/1.0 404 Not Found\r\n"; + status = 404; + } + + } else if (request[0].toUpper() == "POST") { + + // find and validate "Content-Length:" header + int contentLength = -1; + int i = 0; + for (; i < lines.size(); i++) { + if (lines[i].startsWith("Content-Length:", Qt::CaseInsensitive)) + contentLength = lines[i].mid(15).trimmed().toInt(); + if (lines[i].trimmed()=="") + break; + } + if (contentLength < 0) { + responseHead = "HTTP/1.0 411 Length Required\r\n"; + status = 411; + } else if (contentLength > maxPostRequestContentLength) { + responseHead = "HTTP/1.0 413 Payload Too Large\r\n"; + status = 413; + } + + // get the requests body + int postBodyOffset = -1; + for (int i = 0; postBodyOffset < 0 && i < data.size()-4; i++) + if (data[i] == '\r' && data[i+1] == '\n' && data[i+2] == '\r' && data[i+3] == '\n') + postBodyOffset = i+4; + QByteArray postBody; + if (0 <= postBodyOffset && postBodyOffset < data.size()) + postBody = data.mid(postBodyOffset); + + // multi-part message, wait for more packets + while (status == 200 && postBody.size() < contentLength) { + if (socket->waitForReadyRead(2000)) { + QByteArray moreData = socket->readAll(); + #ifdef QT_DEBUG + qDebug() << "QMinimalHttpServer::processTcpData MORE DATA:" << moreData; + #endif + postBody.append(moreData); + } else { + responseHead = "HTTP/1.0 408 Request Timeout\r\n"; + status = 408; + } + } + + // call the handler + if (status == 200) + status = this->processHttpPostRequest(filename, postBody, responseHead, responseBody); + + } else if (request[0].toUpper() == "SUBSCRIBE") { + this->processHttpSubscribeRequest(filename, lines, responseHead, responseBody); + } else if (request[0].toUpper() == "UNSUBSCRIBE") { + this->processHttpUnsubscribeRequest(filename, lines, responseHead, responseBody); + } + + + responseHead += QStringLiteral( + "content-length: ") + QString::number(responseBody.size()) + "\r\n" + "last-modified: Tue, 23 Jan 2018 15:54:00 GMT\r\n" + "date: " + DateRfc7231() + "\r\n" + "connection: close\r\n"; + + #ifdef QT_DEBUG + qDebug() << "QMinimalHttpServer::processTcpData" << responseHead << responseBody; + #endif + + socket->write((responseHead+"\r\n").toUtf8()); + socket->write(responseBody); +} + +/***************************************************************************\ + * Overrides of this method may add headers and set the responseBody. * + * returning true means that the request has successfully been processed * + * returning false means that the (overridden) method could not deliver * + * a response, so then a 404 will be sent. * +\***************************************************************************/ +bool QMinimalHttpServer::processHttpGetRequest(QString urlUpper, QString &responseHead, QByteArray &responseBody) +{ + Q_UNUSED(urlUpper); + Q_UNUSED(responseHead); + Q_UNUSED(responseBody); + return false; +} + +/***************************************************************************\ + * Overrides of this method may add headers and set the responseBody. * + * return value should be an HTTP status code corresponding to the * + * status that is set in the responseHead. * + * Since the method is only invoked if the status was 200 before, * + * returning 200 is valid in cases where responseHead is not changed * +\***************************************************************************/ +int QMinimalHttpServer::processHttpPostRequest(QString urlUpper, QByteArray &postBody, QString &responseHead, QByteArray &responseBody) +{ + Q_UNUSED(urlUpper); + Q_UNUSED(postBody); + Q_UNUSED(responseBody); + responseHead = "HTTP/1.0 403 Forbidden\r\n"; + return 403; +} + +int QMinimalHttpServer::processHttpSubscribeRequest(QString urlUpper, QStringList &requestLines, QString &responseHead, QByteArray &responseBody) +{ + Q_UNUSED(urlUpper); + Q_UNUSED(requestLines); + Q_UNUSED(responseBody); + responseHead = "HTTP/1.0 404 Not Found\r\n"; + return 404; +} + +int QMinimalHttpServer::processHttpUnsubscribeRequest(QString urlUpper, QStringList &requestLines, QString &responseHead, QByteArray &responseBody) +{ + Q_UNUSED(urlUpper); + Q_UNUSED(requestLines); + Q_UNUSED(responseBody); + responseHead = "HTTP/1.0 404 Not Found\r\n"; + return 404; +} + + + +void QMinimalHttpServer::startListening(quint16 port) +{ + if (isListening()) + return; + tcpServer.listen(QHostAddress::Any, port); +} + +void QMinimalHttpServer::stopListening() +{ + if (!isListening()) + return; + tcpServer.close(); +} + +bool QMinimalHttpServer::isListening() +{ + return tcpServer.isListening(); +} + +QString QMinimalHttpServer::DateRfc7231(const QDateTime &date) +{ + static const QString daysEn[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; + static const QString monthsEn[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + QString sDate = daysEn[ date.date().dayOfWeek() - 1 ] + + date.toString(", dd ") + + monthsEn[ date.date().month() - 1 ] + + date.toString(" yyyy HH:mm::ss") + + " GMT"; + return std::move(sDate); +} +QString QMinimalHttpServer::DateRfc7231() +{ + return std::move(DateRfc7231(QDateTime::currentDateTimeUtc())); +} |