integrated webserver ... (#697)

* initial commit of webconfig

* update example config with webconfig and fix format of file
update debian postinst script for install example config
This commit is contained in:
redPanther
2016-06-12 22:27:24 +02:00
committed by brindosch
parent d2f47251f5
commit 7dfb9f1967
55 changed files with 8952 additions and 76 deletions

View File

@@ -19,3 +19,7 @@ add_subdirectory(utils)
add_subdirectory(xbmcvideochecker)
add_subdirectory(effectengine)
add_subdirectory(grabber)
if(ENABLE_QT5)
add_subdirectory(webconfig)
endif()

View File

@@ -17,6 +17,7 @@
#include "hyperion/ImageProcessorFactory.h"
#include "hyperion/ImageProcessor.h"
#include "utils/ColorRgb.h"
#include "HyperionConfig.h"
// project includes
#include "BoblightClientConnection.h"

View File

@@ -16,6 +16,7 @@
// effect engine includes
#include <effectengine/EffectEngine.h>
#include "Effect.h"
#include "HyperionConfig.h"
EffectEngine::EffectEngine(Hyperion * hyperion, const Json::Value & jsonEffectConfig) :
_hyperion(hyperion),

View File

@@ -0,0 +1,53 @@
# Define the current source locations
set(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/webconfig)
set(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/webconfig)
# Group the headers that go through the MOC compiler
set(WebConfig_QT_HEADERS
${CURRENT_SOURCE_DIR}/QtHttpClientWrapper.h
${CURRENT_SOURCE_DIR}/QtHttpHeader.h
${CURRENT_SOURCE_DIR}/QtHttpReply.h
${CURRENT_SOURCE_DIR}/QtHttpRequest.h
${CURRENT_SOURCE_DIR}/QtHttpServer.h
${CURRENT_SOURCE_DIR}/StaticFileServing.h
${CURRENT_HEADER_DIR}/WebConfig.h
)
set(WebConfig_HEADERS
)
set(WebConfig_SOURCES
${CURRENT_SOURCE_DIR}/QtHttpClientWrapper.cpp
${CURRENT_SOURCE_DIR}/QtHttpHeader.cpp
${CURRENT_SOURCE_DIR}/QtHttpReply.cpp
${CURRENT_SOURCE_DIR}/QtHttpRequest.cpp
${CURRENT_SOURCE_DIR}/QtHttpServer.cpp
${CURRENT_SOURCE_DIR}/StaticFileServing.cpp
${CURRENT_SOURCE_DIR}/WebConfig.cpp
)
if(ENABLE_QT5)
qt5_wrap_cpp(WebConfig_HEADERS_MOC ${WebConfig_QT_HEADERS})
else()
qt4_wrap_cpp(WebConfigr_HEADERS_MOC ${WebConfig_QT_HEADERS})
endif()
add_library(webconfig
${WebConfig_HEADERS}
${WebConfig_QT_HEADERS}
${WebConfig_SOURCES}
${WebConfig_HEADERS_MOC}
)
if(ENABLE_QT5)
qt5_use_modules(webconfig Widgets Network)
endif()
target_link_libraries(webconfig
hyperion
hyperion-utils
${QT_LIBRARIES}
)

View File

@@ -0,0 +1,221 @@
#include "QtHttpClientWrapper.h"
#include "QtHttpRequest.h"
#include "QtHttpReply.h"
#include "QtHttpServer.h"
#include "QtHttpHeader.h"
#include <QCryptographicHash>
#include <QTcpSocket>
#include <QStringBuilder>
#include <QStringList>
#include <QDateTime>
const QByteArray & QtHttpClientWrapper::CRLF = QByteArrayLiteral ("\r\n");
QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, QtHttpServer * parent)
: QObject (parent)
, m_guid ("")
, m_parsingStatus (AwaitingRequest)
, m_sockClient (sock)
, m_currentRequest (Q_NULLPTR)
, m_serverHandle (parent)
{
connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
}
QString QtHttpClientWrapper::getGuid (void) {
if (m_guid.isEmpty ()) {
m_guid = QString::fromLocal8Bit (
QCryptographicHash::hash (
QByteArray::number ((quint64) (this)),
QCryptographicHash::Md5
).toHex ()
);
}
return m_guid;
}
void QtHttpClientWrapper::onClientDataReceived (void) {
if (m_sockClient != Q_NULLPTR) {
while (m_sockClient->bytesAvailable ()) {
QByteArray line = m_sockClient->readLine ();
switch (m_parsingStatus) { // handle parsing steps
case AwaitingRequest: { // "command url version" × 1
QString str = QString::fromUtf8 (line).trimmed ();
QStringList parts = str.split (SPACE, QString::SkipEmptyParts);
if (parts.size () == 3) {
QString command = parts.at (0);
QString url = parts.at (1);
QString version = parts.at (2);
if (version == QtHttpServer::HTTP_VERSION) {
//qDebug () << "Debug : HTTP"
// << "command :" << command
// << "url :" << url
// << "version :" << version;
m_currentRequest = new QtHttpRequest (m_serverHandle);
m_currentRequest->setUrl (QUrl (url));
m_currentRequest->setCommand (command);
m_parsingStatus = AwaitingHeaders;
}
else {
m_parsingStatus = ParsingError;
//qWarning () << "Error : unhandled HTTP version :" << version;
}
}
else {
m_parsingStatus = ParsingError;
//qWarning () << "Error : incorrect HTTP command line :" << line;
}
break;
}
case AwaitingHeaders: { // "header: value" × N (until empty line)
QByteArray raw = line.trimmed ();
if (!raw.isEmpty ()) { // parse headers
int pos = raw.indexOf (COLON);
if (pos > 0) {
QByteArray header = raw.left (pos).trimmed ();
QByteArray value = raw.mid (pos +1).trimmed ();
//qDebug () << "Debug : HTTP"
// << "header :" << header
// << "value :" << value;
m_currentRequest->addHeader (header, value);
if (header == QtHttpHeader::ContentLength) {
int len = -1;
bool ok = false;
len = value.toInt (&ok, 10);
if (ok) {
m_currentRequest->addHeader (QtHttpHeader::ContentLength, QByteArray::number (len));
}
}
}
else {
m_parsingStatus = ParsingError;
qWarning () << "Error : incorrect HTTP headers line :" << line;
}
}
else { // end of headers
//qDebug () << "Debug : HTTP end of headers";
if (m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt () > 0) {
m_parsingStatus = AwaitingContent;
}
else {
m_parsingStatus = RequestParsed;
}
}
break;
}
case AwaitingContent: { // raw data × N (until EOF ??)
m_currentRequest->appendRawData (line);
//qDebug () << "Debug : HTTP"
// << "content :" << m_currentRequest->getRawData ().toHex ()
// << "size :" << m_currentRequest->getRawData ().size ();
if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ()) {
//qDebug () << "Debug : HTTP end of content";
m_parsingStatus = RequestParsed;
}
break;
}
default: { break; }
}
switch (m_parsingStatus) { // handle parsing status end/error
case RequestParsed: { // a valid request has ben fully parsed
QtHttpReply reply (m_serverHandle);
connect (&reply, &QtHttpReply::requestSendHeaders,
this, &QtHttpClientWrapper::onReplySendHeadersRequested);
connect (&reply, &QtHttpReply::requestSendData,
this, &QtHttpClientWrapper::onReplySendDataRequested);
emit m_serverHandle->requestNeedsReply (m_currentRequest, &reply); // allow app to handle request
m_parsingStatus = sendReplyToClient (&reply);
break;
}
case ParsingError: { // there was an error durin one of parsing steps
m_sockClient->readAll (); // clear remaining buffer to ignore content
QtHttpReply reply (m_serverHandle);
reply.setStatusCode (QtHttpReply::BadRequest);
reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
reply.appendRawData (CRLF);
m_parsingStatus = sendReplyToClient (&reply);
break;
}
default: { break; }
}
}
}
}
void QtHttpClientWrapper::onReplySendHeadersRequested (void) {
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
if (reply != Q_NULLPTR) {
QByteArray data;
// HTTP Version + Status Code + Status Msg
data.append (QtHttpServer::HTTP_VERSION);
data.append (SPACE);
data.append (QByteArray::number (reply->getStatusCode ()));
data.append (SPACE);
data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ()));
data.append (CRLF);
// Header name: header value
if (reply->useChunked ()) {
static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked");
reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED);
}
else {
reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ()));
}
const QList<QByteArray> & headersList = reply->getHeadersList ();
foreach (const QByteArray & header, headersList) {
data.append (header);
data.append (COLON);
data.append (SPACE);
data.append (reply->getHeader (header));
data.append (CRLF);
}
// empty line
data.append (CRLF);
m_sockClient->write (data);
m_sockClient->flush ();
}
}
void QtHttpClientWrapper::onReplySendDataRequested (void) {
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
if (reply != Q_NULLPTR) {
// content raw data
QByteArray data = reply->getRawData ();
if (reply->useChunked ()) {
data.prepend (QByteArray::number (data.size (), 16) % CRLF);
data.append (CRLF);
reply->resetRawData ();
}
// write to socket
m_sockClient->write (data);
m_sockClient->flush ();
}
}
QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHttpReply * reply) {
if (reply != Q_NULLPTR) {
if (!reply->useChunked ()) {
reply->appendRawData (CRLF);
// send all headers and all data in one shot
reply->requestSendHeaders ();
reply->requestSendData ();
}
else {
// last chunk
m_sockClient->write ("0" % CRLF % CRLF);
m_sockClient->flush ();
}
if (m_currentRequest != Q_NULLPTR) {
static const QByteArray & CLOSE = QByteArrayLiteral ("close");
if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE) {
// must close connection after this request
m_sockClient->close ();
}
m_currentRequest->deleteLater ();
m_currentRequest = Q_NULLPTR;
}
}
return AwaitingRequest;
}

View File

@@ -0,0 +1,51 @@
#ifndef QTHTTPCLIENTWRAPPER_H
#define QTHTTPCLIENTWRAPPER_H
#include <QObject>
#include <QString>
class QTcpSocket;
class QtHttpRequest;
class QtHttpReply;
class QtHttpServer;
class QtHttpClientWrapper : public QObject {
Q_OBJECT
public:
explicit QtHttpClientWrapper (QTcpSocket * sock, QtHttpServer * parent);
static const char SPACE = ' ';
static const char COLON = ':';
static const QByteArray & CRLF;
enum ParsingStatus {
ParsingError = -1,
AwaitingRequest = 0,
AwaitingHeaders = 1,
AwaitingContent = 2,
RequestParsed = 3
};
QString getGuid (void);
private slots:
void onClientDataReceived (void);
protected:
ParsingStatus sendReplyToClient (QtHttpReply * reply);
protected slots:
void onReplySendHeadersRequested (void);
void onReplySendDataRequested (void);
private:
QString m_guid;
ParsingStatus m_parsingStatus;
QTcpSocket * m_sockClient;
QtHttpRequest * m_currentRequest;
QtHttpServer * m_serverHandle;
};
#endif // QTHTTPCLIENTWRAPPER_H

View File

@@ -0,0 +1,32 @@
#include "QtHttpHeader.h"
#include <QByteArray>
const QByteArray & QtHttpHeader::Server = QByteArrayLiteral ("Server");
const QByteArray & QtHttpHeader::Date = QByteArrayLiteral ("Date");
const QByteArray & QtHttpHeader::Host = QByteArrayLiteral ("Host");
const QByteArray & QtHttpHeader::Accept = QByteArrayLiteral ("Accept");
const QByteArray & QtHttpHeader::Cookie = QByteArrayLiteral ("Cookie");
const QByteArray & QtHttpHeader::ContentType = QByteArrayLiteral ("Content-Type");
const QByteArray & QtHttpHeader::ContentLength = QByteArrayLiteral ("Content-Length");
const QByteArray & QtHttpHeader::Connection = QByteArrayLiteral ("Connection");
const QByteArray & QtHttpHeader::UserAgent = QByteArrayLiteral ("User-Agent");
const QByteArray & QtHttpHeader::AcceptCharset = QByteArrayLiteral ("Accept-Charset");
const QByteArray & QtHttpHeader::AcceptEncoding = QByteArrayLiteral ("Accept-Encoding");
const QByteArray & QtHttpHeader::AcceptLanguage = QByteArrayLiteral ("Accept-Language");
const QByteArray & QtHttpHeader::Authorization = QByteArrayLiteral ("Authorization");
const QByteArray & QtHttpHeader::CacheControl = QByteArrayLiteral ("Cache-Control");
const QByteArray & QtHttpHeader::ContentMD5 = QByteArrayLiteral ("Content-MD5");
const QByteArray & QtHttpHeader::ProxyAuthorization = QByteArrayLiteral ("Proxy-Authorization");
const QByteArray & QtHttpHeader::Range = QByteArrayLiteral ("Range");
const QByteArray & QtHttpHeader::ContentEncoding = QByteArrayLiteral ("Content-Encoding");
const QByteArray & QtHttpHeader::ContentLanguage = QByteArrayLiteral ("Content-Language");
const QByteArray & QtHttpHeader::ContentLocation = QByteArrayLiteral ("Content-Location");
const QByteArray & QtHttpHeader::ContentRange = QByteArrayLiteral ("Content-Range");
const QByteArray & QtHttpHeader::Expires = QByteArrayLiteral ("Expires");
const QByteArray & QtHttpHeader::LastModified = QByteArrayLiteral ("Last-Modified");
const QByteArray & QtHttpHeader::Location = QByteArrayLiteral ("Location");
const QByteArray & QtHttpHeader::SetCookie = QByteArrayLiteral ("Set-Cookie");
const QByteArray & QtHttpHeader::TransferEncoding = QByteArrayLiteral ("Transfer-Encoding");
const QByteArray & QtHttpHeader::ContentDisposition = QByteArrayLiteral ("Content-Disposition");

View File

@@ -0,0 +1,37 @@
#ifndef QTHTTPHEADER_H
#define QTHTTPHEADER_H
class QByteArray;
class QtHttpHeader {
public:
static const QByteArray & Server;
static const QByteArray & Date;
static const QByteArray & Host;
static const QByteArray & Accept;
static const QByteArray & ContentType;
static const QByteArray & ContentLength;
static const QByteArray & Connection;
static const QByteArray & Cookie;
static const QByteArray & UserAgent;
static const QByteArray & AcceptCharset;
static const QByteArray & AcceptEncoding;
static const QByteArray & AcceptLanguage;
static const QByteArray & Authorization;
static const QByteArray & CacheControl;
static const QByteArray & ContentMD5;
static const QByteArray & ProxyAuthorization;
static const QByteArray & Range;
static const QByteArray & ContentEncoding;
static const QByteArray & ContentLanguage;
static const QByteArray & ContentLocation;
static const QByteArray & ContentRange;
static const QByteArray & Expires;
static const QByteArray & LastModified;
static const QByteArray & Location;
static const QByteArray & SetCookie;
static const QByteArray & TransferEncoding;
static const QByteArray & ContentDisposition;
};
#endif // QTHTTPHEADER_H

View File

@@ -0,0 +1,75 @@
#include "QtHttpReply.h"
#include "QtHttpHeader.h"
#include "QtHttpServer.h"
#include <QDateTime>
QtHttpReply::QtHttpReply (QtHttpServer * parent)
: QObject (parent)
, m_useChunked (false)
, m_statusCode (Ok)
, m_data (QByteArray ())
, m_serverHandle (parent)
{
// set some additional headers
addHeader (QtHttpHeader::Date, QDateTime::currentDateTimeUtc ().toString ("ddd, dd MMM yyyy hh:mm:ss t").toUtf8 ());
addHeader (QtHttpHeader::Server, m_serverHandle->getServerName ().toUtf8 ());
}
int QtHttpReply::getRawDataSize (void) const {
return m_data.size ();
}
bool QtHttpReply::useChunked (void) const {
return m_useChunked;
}
QtHttpReply::StatusCode QtHttpReply::getStatusCode (void) const {
return m_statusCode;
}
QByteArray QtHttpReply::getRawData (void) const {
return m_data;
}
QList<QByteArray> QtHttpReply::getHeadersList (void) const {
return m_headersHash.keys ();
}
QByteArray QtHttpReply::getHeader (const QByteArray & header) const {
return m_headersHash.value (header, QByteArray ());
}
const QByteArray QtHttpReply::getStatusTextForCode (QtHttpReply::StatusCode statusCode) {
switch (statusCode) {
case Ok: return QByteArrayLiteral ("OK.");
case BadRequest: return QByteArrayLiteral ("Bad request !");
case Forbidden: return QByteArrayLiteral ("Forbidden !");
case NotFound: return QByteArrayLiteral ("Not found !");
default: return QByteArrayLiteral ("");
}
}
void QtHttpReply::setUseChunked (bool chunked){
m_useChunked = chunked;
}
void QtHttpReply::setStatusCode (QtHttpReply::StatusCode statusCode) {
m_statusCode = statusCode;
}
void QtHttpReply::appendRawData (const QByteArray & data) {
m_data.append (data);
}
void QtHttpReply::addHeader (const QByteArray & header, const QByteArray & value) {
QByteArray key = header.trimmed ();
if (!key.isEmpty ()) {
m_headersHash.insert (key, value);
}
}
void QtHttpReply::resetRawData (void) {
m_data.clear ();
}

View File

@@ -0,0 +1,55 @@
#ifndef QTHTTPREPLY_H
#define QTHTTPREPLY_H
#include <QObject>
#include <QByteArray>
#include <QHash>
#include <QList>
class QtHttpServer;
class QtHttpReply : public QObject {
Q_OBJECT
Q_ENUMS (StatusCode)
public:
explicit QtHttpReply (QtHttpServer * parent);
enum StatusCode {
Ok = 200,
BadRequest = 400,
Forbidden = 403,
NotFound = 404,
InternalError = 502,
};
int getRawDataSize (void) const;
bool useChunked (void) const;
StatusCode getStatusCode (void) const;
QByteArray getRawData (void) const;
QList<QByteArray> getHeadersList (void) const;
QByteArray getHeader (const QByteArray & header) const;
static const QByteArray getStatusTextForCode (StatusCode statusCode);
public slots:
void setUseChunked (bool chunked = false);
void setStatusCode (StatusCode statusCode);
void appendRawData (const QByteArray & data);
void addHeader (const QByteArray & header, const QByteArray & value);
void resetRawData (void);
signals:
void requestSendHeaders (void);
void requestSendData (void);
private:
bool m_useChunked;
StatusCode m_statusCode;
QByteArray m_data;
QtHttpServer * m_serverHandle;
QHash<QByteArray, QByteArray> m_headersHash;
};
#endif // QTHTTPREPLY_H

View File

@@ -0,0 +1,60 @@
#include "QtHttpRequest.h"
#include "QtHttpHeader.h"
#include "QtHttpServer.h"
QtHttpRequest::QtHttpRequest (QtHttpServer * parent)
: QObject (parent)
, m_url (QUrl ())
, m_command (QString ())
, m_data (QByteArray ())
, m_serverHandle (parent)
{
// set some additional headers
addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0"));
addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive"));
}
QUrl QtHttpRequest::getUrl (void) const {
return m_url;
}
QString QtHttpRequest::getCommand (void) const {
return m_command;
}
int QtHttpRequest::getRawDataSize (void) const {
return m_data.size ();
}
QByteArray QtHttpRequest::getRawData (void) const {
return m_data;
}
QList<QByteArray> QtHttpRequest::getHeadersList (void) const {
return m_headersHash.keys ();
}
QByteArray QtHttpRequest::getHeader (const QByteArray & header) const {
return m_headersHash.value (header, QByteArray ());
}
void QtHttpRequest::setUrl (const QUrl & url) {
m_url = url;
}
void QtHttpRequest::setCommand (const QString & command) {
m_command = command;
}
void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value) {
QByteArray key = header.trimmed ();
if (!key.isEmpty ()) {
m_headersHash.insert (key, value);
}
}
void QtHttpRequest::appendRawData (const QByteArray & data) {
m_data.append (data);
}

View File

@@ -0,0 +1,40 @@
#ifndef QTHTTPREQUEST_H
#define QTHTTPREQUEST_H
#include <QObject>
#include <QString>
#include <QByteArray>
#include <QHash>
#include <QUrl>
class QtHttpServer;
class QtHttpRequest : public QObject {
Q_OBJECT
public:
explicit QtHttpRequest (QtHttpServer * parent);
int getRawDataSize (void) const;
QUrl getUrl (void) const;
QString getCommand (void) const;
QByteArray getRawData (void) const;
QList<QByteArray> getHeadersList (void) const;
QByteArray getHeader (const QByteArray & header) const;
public slots:
void setUrl (const QUrl & url);
void setCommand (const QString & command);
void addHeader (const QByteArray & header, const QByteArray & value);
void appendRawData (const QByteArray & data);
private:
QUrl m_url;
QString m_command;
QByteArray m_data;
QtHttpServer * m_serverHandle;
QHash<QByteArray, QByteArray> m_headersHash;
};
#endif // QTHTTPREQUEST_H

View File

@@ -0,0 +1,66 @@
#include "QtHttpServer.h"
#include "QtHttpRequest.h"
#include "QtHttpReply.h"
#include "QtHttpClientWrapper.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QDebug>
const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1");
QtHttpServer::QtHttpServer (QObject * parent)
: QObject (parent)
, m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
{
m_sockServer = new QTcpServer (this);
connect (m_sockServer, &QTcpServer::newConnection, this, &QtHttpServer::onClientConnected);
}
const QString QtHttpServer::getServerName (void) const {
return m_serverName;
}
void QtHttpServer::start (quint16 port) {
if (m_sockServer->listen (QHostAddress::Any, port)) {
emit started (m_sockServer->serverPort ());
}
else {
emit error (m_sockServer->errorString ());
}
}
void QtHttpServer::stop (void) {
if (m_sockServer->isListening ()) {
m_sockServer->close ();
emit stopped ();
}
}
void QtHttpServer::setServerName (const QString & serverName) {
m_serverName = serverName;
}
void QtHttpServer::onClientConnected (void) {
while (m_sockServer->hasPendingConnections ()) {
QTcpSocket * sockClient = m_sockServer->nextPendingConnection ();
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sockClient, this);
connect (sockClient, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
m_socksClientsHash.insert (sockClient, wrapper);
emit clientConnected (wrapper->getGuid ());
}
}
void QtHttpServer::onClientDisconnected (void) {
QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ());
if (sockClient) {
QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR);
if (wrapper) {
emit clientDisconnected (wrapper->getGuid ());
wrapper->deleteLater ();
m_socksClientsHash.remove (sockClient);
}
}
}

View File

@@ -0,0 +1,48 @@
#ifndef QTHTTPSERVER_H
#define QTHTTPSERVER_H
#include <QObject>
#include <QString>
#include <QHash>
class QTcpSocket;
class QTcpServer;
class QtHttpRequest;
class QtHttpReply;
class QtHttpClientWrapper;
class QtHttpServer : public QObject {
Q_OBJECT
public:
explicit QtHttpServer (QObject * parent = Q_NULLPTR);
static const QString & HTTP_VERSION;
const QString getServerName (void) const;
public slots:
void start (quint16 port = 0);
void stop (void);
void setServerName (const QString & serverName);
signals:
void started (quint16 port);
void stopped (void);
void error (const QString & msg);
void clientConnected (const QString & guid);
void clientDisconnected (const QString & guid);
void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
private slots:
void onClientConnected (void);
void onClientDisconnected (void);
private:
QString m_serverName;
QTcpServer * m_sockServer;
QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash;
};
#endif // QTHTTPSERVER_H

View File

@@ -0,0 +1,87 @@
#include "StaticFileServing.h"
#include <QStringBuilder>
#include <QUrlQuery>
#include <QDebug>
#include <QList>
#include <QPair>
#include <QFile>
StaticFileServing::StaticFileServing (QString baseUrl, quint16 port, QObject * parent)
: QObject (parent)
, m_baseUrl (baseUrl)
{
m_mimeDb = new QMimeDatabase;
m_server = new QtHttpServer (this);
m_server->setServerName (QStringLiteral ("Qt Static HTTP File Server"));
connect (m_server, &QtHttpServer::started, this, &StaticFileServing::onServerStarted);
connect (m_server, &QtHttpServer::stopped, this, &StaticFileServing::onServerStopped);
connect (m_server, &QtHttpServer::error, this, &StaticFileServing::onServerError);
connect (m_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);
m_server->start (port);
}
StaticFileServing::~StaticFileServing ()
{
m_server->stop ();
}
void StaticFileServing::onServerStarted (quint16 port)
{
qDebug () << "QtHttpServer started on port" << port << m_server->getServerName ();
}
void StaticFileServing::onServerStopped () {
qDebug () << "QtHttpServer stopped" << m_server->getServerName ();
}
void StaticFileServing::onServerError (QString msg)
{
qDebug () << "QtHttpServer error :" << msg;
}
static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
{
reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
reply->appendRawData (errorMessage.toLocal8Bit ());
}
void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
{
QString command = request->getCommand ();
if (command == QStringLiteral ("GET"))
{
QString path = request->getUrl ().path ();
if ( path == "/" || path.isEmpty() || ! QFile::exists(m_baseUrl % "/" % path) )
path = "index.html";
QFile file (m_baseUrl % "/" % path);
if (file.exists ())
{
QMimeType mime = m_mimeDb->mimeTypeForFile (file.fileName ());
if (file.open (QFile::ReadOnly)) {
QByteArray data = file.readAll ();
reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
reply->appendRawData (data);
file.close ();
}
else
{
printErrorToReply (reply, "Requested file " % m_baseUrl % "/" % path % " couldn't be open for reading !");
}
}
else
{
printErrorToReply (reply, "Requested file " % path % " couldn't be found !");
}
}
else
{
printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on static file server !");
}
}

View File

@@ -0,0 +1,31 @@
#ifndef STATICFILESERVING_H
#define STATICFILESERVING_H
#include <QObject>
#include <QMimeDatabase>
#include "QtHttpServer.h"
#include "QtHttpRequest.h"
#include "QtHttpReply.h"
#include "QtHttpHeader.h"
class StaticFileServing : public QObject {
Q_OBJECT
public:
explicit StaticFileServing (QString baseUrl, quint16 port, QObject * parent = NULL);
virtual ~StaticFileServing (void);
public slots:
void onServerStopped (void);
void onServerStarted (quint16 port);
void onServerError (QString msg);
void onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
private:
QString m_baseUrl;
QtHttpServer * m_server;
QMimeDatabase * m_mimeDb;
};
#endif // STATICFILESERVING_H

View File

@@ -0,0 +1,33 @@
#include "webconfig/webconfig.h"
#include "StaticFileServing.h"
WebConfig::WebConfig(std::string baseUrl, quint16 port, QObject * parent) :
_parent(parent),
_baseUrl(QString::fromStdString(baseUrl)),
_port(port),
_server(nullptr)
{
}
WebConfig::~WebConfig()
{
stop();
}
void WebConfig::start()
{
if ( _server == nullptr )
_server = new StaticFileServing (_baseUrl, _port, this);
}
void WebConfig::stop()
{
if ( _server != nullptr )
{
delete _server;
_server = nullptr;
}
}