mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
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:
@@ -19,3 +19,7 @@ add_subdirectory(utils)
|
||||
add_subdirectory(xbmcvideochecker)
|
||||
add_subdirectory(effectengine)
|
||||
add_subdirectory(grabber)
|
||||
|
||||
if(ENABLE_QT5)
|
||||
add_subdirectory(webconfig)
|
||||
endif()
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "hyperion/ImageProcessorFactory.h"
|
||||
#include "hyperion/ImageProcessor.h"
|
||||
#include "utils/ColorRgb.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
// project includes
|
||||
#include "BoblightClientConnection.h"
|
||||
|
@@ -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),
|
||||
|
53
libsrc/webconfig/CMakeLists.txt
Normal file
53
libsrc/webconfig/CMakeLists.txt
Normal 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}
|
||||
)
|
||||
|
||||
|
221
libsrc/webconfig/QtHttpClientWrapper.cpp
Normal file
221
libsrc/webconfig/QtHttpClientWrapper.cpp
Normal 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;
|
||||
}
|
51
libsrc/webconfig/QtHttpClientWrapper.h
Normal file
51
libsrc/webconfig/QtHttpClientWrapper.h
Normal 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
|
32
libsrc/webconfig/QtHttpHeader.cpp
Normal file
32
libsrc/webconfig/QtHttpHeader.cpp
Normal 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");
|
37
libsrc/webconfig/QtHttpHeader.h
Normal file
37
libsrc/webconfig/QtHttpHeader.h
Normal 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
|
75
libsrc/webconfig/QtHttpReply.cpp
Normal file
75
libsrc/webconfig/QtHttpReply.cpp
Normal 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 ();
|
||||
}
|
55
libsrc/webconfig/QtHttpReply.h
Normal file
55
libsrc/webconfig/QtHttpReply.h
Normal 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
|
60
libsrc/webconfig/QtHttpRequest.cpp
Normal file
60
libsrc/webconfig/QtHttpRequest.cpp
Normal 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);
|
||||
}
|
40
libsrc/webconfig/QtHttpRequest.h
Normal file
40
libsrc/webconfig/QtHttpRequest.h
Normal 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
|
66
libsrc/webconfig/QtHttpServer.cpp
Normal file
66
libsrc/webconfig/QtHttpServer.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
48
libsrc/webconfig/QtHttpServer.h
Normal file
48
libsrc/webconfig/QtHttpServer.h
Normal 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
|
87
libsrc/webconfig/StaticFileServing.cpp
Normal file
87
libsrc/webconfig/StaticFileServing.cpp
Normal 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 !");
|
||||
}
|
||||
}
|
||||
|
31
libsrc/webconfig/StaticFileServing.h
Normal file
31
libsrc/webconfig/StaticFileServing.h
Normal 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
|
33
libsrc/webconfig/WebConfig.cpp
Normal file
33
libsrc/webconfig/WebConfig.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user