The slot in the websocket client will now run through until there are no more data in the buffer

Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
Paulchen-Panther 2019-07-21 15:03:50 +02:00
parent 370e1b5f45
commit 96d79cdef6
No known key found for this signature in database
GPG Key ID: 84E3B692456B6840
14 changed files with 652 additions and 661 deletions

View File

@ -298,7 +298,7 @@ $(document).ready(function() {
createCP('cp2', cpcolor, function(rgbT,hex){ createCP('cp2', cpcolor, function(rgbT,hex){
rgb = rgbT; rgb = rgbT;
sendColor() sendColor();
setStorage('rmcpcolor', hex); setStorage('rmcpcolor', hex);
}); });

View File

@ -180,8 +180,6 @@ void LinearColorSmoothing::componentStateChange(const hyperion::Components compo
void LinearColorSmoothing::setEnable(bool enable) void LinearColorSmoothing::setEnable(bool enable)
{ {
LedDevice::setEnable(enable);
if (!enable) if (!enable)
{ {
_timer->stop(); _timer->stop();

View File

@ -27,93 +27,131 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, const bool& localCo
, m_websocketClient(nullptr) , m_websocketClient(nullptr)
, m_webJsonRpc (nullptr) , m_webJsonRpc (nullptr)
{ {
connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived); connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
} }
QString QtHttpClientWrapper::getGuid (void) { QString QtHttpClientWrapper::getGuid (void)
if (m_guid.isEmpty ()) { {
m_guid = QString::fromLocal8Bit ( if (m_guid.isEmpty ())
QCryptographicHash::hash ( {
QByteArray::number ((quint64) (this)), m_guid = QString::fromLocal8Bit (
QCryptographicHash::Md5 QCryptographicHash::hash (
).toHex () QByteArray::number ((quint64) (this)),
); QCryptographicHash::Md5
} ).toHex ()
return m_guid; );
}
return m_guid;
} }
void QtHttpClientWrapper::onClientDataReceived (void) { void QtHttpClientWrapper::onClientDataReceived (void)
if (m_sockClient != Q_NULLPTR) { {
while (m_sockClient->bytesAvailable ()) { if (m_sockClient != Q_NULLPTR)
QByteArray line = m_sockClient->readLine (); {
switch (m_parsingStatus) { // handle parsing steps while (m_sockClient->bytesAvailable ())
case AwaitingRequest: { // "command url version" × 1 {
QString str = QString::fromUtf8 (line).trimmed (); QByteArray line = m_sockClient->readLine ();
QStringList parts = str.split (SPACE, QString::SkipEmptyParts);
if (parts.size () == 3) { switch (m_parsingStatus) // handle parsing steps
QString command = parts.at (0); {
QString url = parts.at (1); case AwaitingRequest: // "command url version" × 1
QString version = parts.at (2); {
if (version == QtHttpServer::HTTP_VERSION) { QString str = QString::fromUtf8 (line).trimmed ();
m_currentRequest = new QtHttpRequest (this, m_serverHandle); QStringList parts = str.split (SPACE, QString::SkipEmptyParts);
m_currentRequest->setClientInfo(m_sockClient->localAddress(), m_sockClient->peerAddress());
m_currentRequest->setUrl (QUrl (url)); if (parts.size () == 3)
m_currentRequest->setCommand (command); {
m_parsingStatus = AwaitingHeaders; QString command = parts.at (0);
} QString url = parts.at (1);
else { QString version = parts.at (2);
m_parsingStatus = ParsingError;
//qWarning () << "Error : unhandled HTTP version :" << version; if (version == QtHttpServer::HTTP_VERSION)
} {
} m_currentRequest = new QtHttpRequest (this, m_serverHandle);
else { m_currentRequest->setClientInfo(m_sockClient->localAddress(), m_sockClient->peerAddress());
m_parsingStatus = ParsingError; m_currentRequest->setUrl (QUrl (url));
//qWarning () << "Error : incorrect HTTP command line :" << line; m_currentRequest->setCommand (command);
} m_parsingStatus = AwaitingHeaders;
break; }
} else
case AwaitingHeaders: { // "header: value" × N (until empty line) {
QByteArray raw = line.trimmed (); m_parsingStatus = ParsingError;
if (!raw.isEmpty ()) { // parse headers //qWarning () << "Error : unhandled HTTP version :" << version;
int pos = raw.indexOf (COLON); }
if (pos > 0) { }
QByteArray header = raw.left (pos).trimmed (); else
QByteArray value = raw.mid (pos +1).trimmed (); {
m_currentRequest->addHeader (header, value); m_parsingStatus = ParsingError;
if (header == QtHttpHeader::ContentLength) { //qWarning () << "Error : incorrect HTTP command line :" << line;
bool ok = false; }
const int len = value.toInt (&ok, 10);
if (ok) { break;
m_currentRequest->addHeader (QtHttpHeader::ContentLength, QByteArray::number (len)); }
} case AwaitingHeaders: // "header: value" × N (until empty line)
} {
} QByteArray raw = line.trimmed ();
else {
m_parsingStatus = ParsingError; if (!raw.isEmpty ()) // parse headers
qWarning () << "Error : incorrect HTTP headers line :" << line; {
} int pos = raw.indexOf (COLON);
}
else { // end of headers if (pos > 0)
if (m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt () > 0) { {
m_parsingStatus = AwaitingContent; QByteArray header = raw.left (pos).trimmed ();
} QByteArray value = raw.mid (pos +1).trimmed ();
else { m_currentRequest->addHeader (header, value);
m_parsingStatus = RequestParsed; if (header == QtHttpHeader::ContentLength)
} {
} bool ok = false;
break; const int len = value.toInt (&ok, 10);
} if (ok)
case AwaitingContent: { // raw data × N (until EOF ??) {
m_currentRequest->appendRawData (line); m_currentRequest->addHeader (QtHttpHeader::ContentLength, QByteArray::number (len));
if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ()) { }
m_parsingStatus = RequestParsed; }
} }
break; else
} {
default: { break; } m_parsingStatus = ParsingError;
} qWarning () << "Error : incorrect HTTP headers line :" << line;
switch (m_parsingStatus) { // handle parsing status end/error }
case RequestParsed: { // a valid request has ben fully parsed }
else // 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);
if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ())
{
m_parsingStatus = RequestParsed;
}
break;
}
default:
{
break;
}
}
switch (m_parsingStatus) // handle parsing status end/error
{
case RequestParsed: // a valid request has ben fully parsed
{
// Catch websocket header "Upgrade" // Catch websocket header "Upgrade"
if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket") if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket")
{ {
@ -121,146 +159,184 @@ void QtHttpClientWrapper::onClientDataReceived (void) {
{ {
// disconnect this slot from socket for further requests // disconnect this slot from socket for further requests
disconnect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived); disconnect(m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived);
// disabling packet bunching
m_sockClient->setSocketOption(QAbstractSocket::LowDelayOption, 1);
m_sockClient->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
m_websocketClient = new WebSocketClient(m_currentRequest, m_sockClient, m_localConnection, this); m_websocketClient = new WebSocketClient(m_currentRequest, m_sockClient, m_localConnection, this);
} }
break; break;
} }
// add post data to request and catch /jsonrpc subroute url // add post data to request and catch /jsonrpc subroute url
if ( m_currentRequest->getCommand() == "POST") if ( m_currentRequest->getCommand() == "POST")
{ {
QtHttpPostData postData; QtHttpPostData postData;
QByteArray data = m_currentRequest->getRawData(); QByteArray data = m_currentRequest->getRawData();
QList<QByteArray> parts = data.split('&'); QList<QByteArray> parts = data.split('&');
for (int i = 0; i < parts.size(); ++i)
{ for (int i = 0; i < parts.size(); ++i)
QList<QByteArray> keyValue = parts.at(i).split('='); {
QByteArray value; QList<QByteArray> keyValue = parts.at(i).split('=');
if (keyValue.size()>1) QByteArray value;
{
value = QByteArray::fromPercentEncoding(keyValue.at(1)); if (keyValue.size()>1)
} {
postData.insert(QString::fromUtf8(keyValue.at(0)),value); value = QByteArray::fromPercentEncoding(keyValue.at(1));
} }
m_currentRequest->setPostData(postData);
postData.insert(QString::fromUtf8(keyValue.at(0)),value);
}
m_currentRequest->setPostData(postData);
// catch /jsonrpc in url, we need async callback, StaticFileServing is sync // catch /jsonrpc in url, we need async callback, StaticFileServing is sync
QString path = m_currentRequest->getUrl ().path (); QString path = m_currentRequest->getUrl ().path ();
QStringList uri_parts = path.split('/', QString::SkipEmptyParts); QStringList uri_parts = path.split('/', QString::SkipEmptyParts);
if ( ! uri_parts.empty() && uri_parts.at(0) == "json-rpc" ) if ( ! uri_parts.empty() && uri_parts.at(0) == "json-rpc" )
{ {
if(m_webJsonRpc == Q_NULLPTR) if(m_webJsonRpc == Q_NULLPTR)
{ {
m_webJsonRpc = new WebJsonRpc(m_currentRequest, m_serverHandle, m_localConnection, this); m_webJsonRpc = new WebJsonRpc(m_currentRequest, m_serverHandle, m_localConnection, this);
} }
m_webJsonRpc->handleMessage(m_currentRequest); m_webJsonRpc->handleMessage(m_currentRequest);
break; break;
} }
} }
QtHttpReply reply (m_serverHandle); QtHttpReply reply (m_serverHandle);
connect (&reply, &QtHttpReply::requestSendHeaders, connect (&reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested);
this, &QtHttpClientWrapper::onReplySendHeadersRequested); connect (&reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested);
connect (&reply, &QtHttpReply::requestSendData, emit m_serverHandle->requestNeedsReply (m_currentRequest, &reply); // allow app to handle request
this, &QtHttpClientWrapper::onReplySendDataRequested);
emit m_serverHandle->requestNeedsReply (m_currentRequest, &reply); // allow app to handle request
m_parsingStatus = sendReplyToClient (&reply); m_parsingStatus = sendReplyToClient (&reply);
break;
} break;
case ParsingError: { // there was an error durin one of parsing steps }
m_sockClient->readAll (); // clear remaining buffer to ignore content case ParsingError: // there was an error durin one of parsing steps
QtHttpReply reply (m_serverHandle); {
reply.setStatusCode (QtHttpReply::BadRequest); m_sockClient->readAll (); // clear remaining buffer to ignore content
reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>")); QtHttpReply reply (m_serverHandle);
reply.appendRawData (CRLF); reply.setStatusCode (QtHttpReply::BadRequest);
m_parsingStatus = sendReplyToClient (&reply); reply.appendRawData (QByteArrayLiteral ("<h1>Bad Request (HTTP parsing error) !</h1>"));
break; reply.appendRawData (CRLF);
} m_parsingStatus = sendReplyToClient (&reply);
default: { break; }
} break;
} }
} default:
{
break;
}
}
}
}
} }
void QtHttpClientWrapper::onReplySendHeadersRequested (void) { void QtHttpClientWrapper::onReplySendHeadersRequested (void)
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ()); {
if (reply != Q_NULLPTR) { QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
QByteArray data;
// HTTP Version + Status Code + Status Msg if (reply != Q_NULLPTR)
data.append (QtHttpServer::HTTP_VERSION); {
data.append (SPACE); QByteArray data;
data.append (QByteArray::number (reply->getStatusCode ())); // HTTP Version + Status Code + Status Msg
data.append (SPACE); data.append (QtHttpServer::HTTP_VERSION);
data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ())); data.append (SPACE);
data.append (CRLF); data.append (QByteArray::number (reply->getStatusCode ()));
// Header name: header value data.append (SPACE);
if (reply->useChunked ()) { data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ()));
static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked"); data.append (CRLF);
reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED);
} if (reply->useChunked ()) // Header name: header value
else { {
reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ())); static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked");
} reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED);
const QList<QByteArray> & headersList = reply->getHeadersList (); }
foreach (const QByteArray & header, headersList) { else
data.append (header); {
data.append (COLON); reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ()));
data.append (SPACE); }
data.append (reply->getHeader (header));
data.append (CRLF); const QList<QByteArray> & headersList = reply->getHeadersList ();
}
// empty line foreach (const QByteArray & header, headersList)
data.append (CRLF); {
m_sockClient->write (data); data.append (header);
m_sockClient->flush (); 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) { void QtHttpClientWrapper::onReplySendDataRequested (void)
QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ()); {
if (reply != Q_NULLPTR) { QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
// content raw data if (reply != Q_NULLPTR)
QByteArray data = reply->getRawData (); {
if (reply->useChunked ()) { // content raw data
data.prepend (QByteArray::number (data.size (), 16) % CRLF); QByteArray data = reply->getRawData ();
data.append (CRLF);
reply->resetRawData (); if (reply->useChunked ())
} {
// write to socket data.prepend (QByteArray::number (data.size (), 16) % CRLF);
m_sockClient->write (data); data.append (CRLF);
m_sockClient->flush (); reply->resetRawData ();
} }
// write to socket
m_sockClient->write (data);
m_sockClient->flush ();
}
} }
void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply) { void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply)
connect (reply, &QtHttpReply::requestSendHeaders, {
this, &QtHttpClientWrapper::onReplySendHeadersRequested); connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested);
connect (reply, &QtHttpReply::requestSendData, connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested);
this, &QtHttpClientWrapper::onReplySendDataRequested);
m_parsingStatus = sendReplyToClient (reply); m_parsingStatus = sendReplyToClient (reply);
} }
QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHttpReply * reply) { QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHttpReply * reply)
if (reply != Q_NULLPTR) { {
if (!reply->useChunked ()) { if (reply != Q_NULLPTR)
//reply->appendRawData (CRLF); {
// send all headers and all data in one shot if (!reply->useChunked ())
reply->requestSendHeaders (); {
reply->requestSendData (); //reply->appendRawData (CRLF);
} // send all headers and all data in one shot
else { reply->requestSendHeaders ();
// last chunk reply->requestSendData ();
m_sockClient->write ("0" % CRLF % CRLF); }
m_sockClient->flush (); else
} {
if (m_currentRequest != Q_NULLPTR) { // last chunk
static const QByteArray & CLOSE = QByteArrayLiteral ("close"); m_sockClient->write ("0" % CRLF % CRLF);
if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE) { m_sockClient->flush ();
// must close connection after this request }
m_sockClient->close ();
} if (m_currentRequest != Q_NULLPTR)
m_currentRequest->deleteLater (); {
m_currentRequest = Q_NULLPTR; static const QByteArray & CLOSE = QByteArrayLiteral ("close");
}
} if (m_currentRequest->getHeader (QtHttpHeader::Connection).toLower () == CLOSE)
return AwaitingRequest; {
// must close connection after this request
m_sockClient->close ();
}
m_currentRequest->deleteLater ();
m_currentRequest = Q_NULLPTR;
}
}
return AwaitingRequest;
} }

View File

@ -3,35 +3,36 @@
class QByteArray; class QByteArray;
class QtHttpHeader { class QtHttpHeader
{
public: public:
static const QByteArray & Server; static const QByteArray & Server;
static const QByteArray & Date; static const QByteArray & Date;
static const QByteArray & Host; static const QByteArray & Host;
static const QByteArray & Accept; static const QByteArray & Accept;
static const QByteArray & ContentType; static const QByteArray & ContentType;
static const QByteArray & ContentLength; static const QByteArray & ContentLength;
static const QByteArray & Connection; static const QByteArray & Connection;
static const QByteArray & Cookie; static const QByteArray & Cookie;
static const QByteArray & UserAgent; static const QByteArray & UserAgent;
static const QByteArray & AcceptCharset; static const QByteArray & AcceptCharset;
static const QByteArray & AcceptEncoding; static const QByteArray & AcceptEncoding;
static const QByteArray & AcceptLanguage; static const QByteArray & AcceptLanguage;
static const QByteArray & Authorization; static const QByteArray & Authorization;
static const QByteArray & CacheControl; static const QByteArray & CacheControl;
static const QByteArray & ContentMD5; static const QByteArray & ContentMD5;
static const QByteArray & ProxyAuthorization; static const QByteArray & ProxyAuthorization;
static const QByteArray & Range; static const QByteArray & Range;
static const QByteArray & ContentEncoding; static const QByteArray & ContentEncoding;
static const QByteArray & ContentLanguage; static const QByteArray & ContentLanguage;
static const QByteArray & ContentLocation; static const QByteArray & ContentLocation;
static const QByteArray & ContentRange; static const QByteArray & ContentRange;
static const QByteArray & Expires; static const QByteArray & Expires;
static const QByteArray & LastModified; static const QByteArray & LastModified;
static const QByteArray & Location; static const QByteArray & Location;
static const QByteArray & SetCookie; static const QByteArray & SetCookie;
static const QByteArray & TransferEncoding; static const QByteArray & TransferEncoding;
static const QByteArray & ContentDisposition; static const QByteArray & ContentDisposition;
static const QByteArray & AccessControlAllow; static const QByteArray & AccessControlAllow;
// Websocket specific headers // Websocket specific headers
static const QByteArray & Upgrade; static const QByteArray & Upgrade;

View File

@ -6,70 +6,35 @@
#include <QDateTime> #include <QDateTime>
QtHttpReply::QtHttpReply (QtHttpServer * parent) QtHttpReply::QtHttpReply (QtHttpServer * parent)
: QObject (parent) : QObject (parent)
, m_useChunked (false) , m_useChunked (false)
, m_statusCode (Ok) , m_statusCode (Ok)
, m_data (QByteArray ()) , m_data (QByteArray ())
, m_serverHandle (parent) , m_serverHandle (parent)
{ {
// set some additional headers // set some additional headers
addHeader (QtHttpHeader::Date, QDateTime::currentDateTimeUtc ().toString ("ddd, dd MMM yyyy hh:mm:ss t").toUtf8 ()); addHeader (QtHttpHeader::Date, QDateTime::currentDateTimeUtc ().toString ("ddd, dd MMM yyyy hh:mm:ss t").toUtf8 ());
addHeader (QtHttpHeader::Server, m_serverHandle->getServerName ().toUtf8 ()); addHeader (QtHttpHeader::Server, m_serverHandle->getServerName ().toUtf8 ());
} }
int QtHttpReply::getRawDataSize (void) const { const QByteArray QtHttpReply::getStatusTextForCode (QtHttpReply::StatusCode statusCode)
return m_data.size (); {
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 ("");
}
} }
bool QtHttpReply::useChunked (void) const { void QtHttpReply::addHeader (const QByteArray & header, const QByteArray & value)
return m_useChunked; {
} QByteArray key = header.trimmed ();
QtHttpReply::StatusCode QtHttpReply::getStatusCode (void) const { if (!key.isEmpty ())
return m_statusCode; {
} m_headersHash.insert (key, value);
}
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

@ -8,53 +8,58 @@
class QtHttpServer; class QtHttpServer;
class QtHttpReply : public QObject { class QtHttpReply : public QObject
Q_OBJECT {
Q_ENUMS (StatusCode) Q_OBJECT
Q_ENUMS (StatusCode)
public: public:
explicit QtHttpReply (QtHttpServer * parent); explicit QtHttpReply (QtHttpServer * parent);
enum StatusCode { enum StatusCode
Ok = 200, {
SeeOther = 303, Ok = 200,
BadRequest = 400, SeeOther = 303,
Forbidden = 403, BadRequest = 400,
NotFound = 404, Forbidden = 403,
MethodNotAllowed = 405, NotFound = 404,
InternalError = 500, MethodNotAllowed = 405,
NotImplemented = 501, InternalError = 500,
BadGateway = 502, NotImplemented = 501,
ServiceUnavailable = 503, BadGateway = 502,
}; ServiceUnavailable = 503,
};
int getRawDataSize (void) const; int getRawDataSize (void) const { return m_data.size(); };
bool useChunked (void) const; bool useChunked (void) const { return m_useChunked; };
StatusCode getStatusCode (void) const; StatusCode getStatusCode (void) const { return m_statusCode; };
QByteArray getRawData (void) const; QByteArray getRawData (void) const { return m_data; };
QList<QByteArray> getHeadersList (void) const; QList<QByteArray> getHeadersList (void) const { return m_headersHash.keys (); };
QByteArray getHeader (const QByteArray & header) const; QByteArray getHeader (const QByteArray & header) const
{
return m_headersHash.value (header, QByteArray ());
};
static const QByteArray getStatusTextForCode (StatusCode statusCode); static const QByteArray getStatusTextForCode (StatusCode statusCode);
public slots: public slots:
void setUseChunked (bool chunked = false); void setUseChunked (bool chunked = false) { m_useChunked = chunked; };
void setStatusCode (StatusCode statusCode); void setStatusCode (StatusCode statusCode) { m_statusCode = statusCode; };
void appendRawData (const QByteArray & data); void appendRawData (const QByteArray & data) { m_data.append(data); };
void addHeader (const QByteArray & header, const QByteArray & value); void addHeader (const QByteArray & header, const QByteArray & value);
void resetRawData (void); void resetRawData (void) { m_data.clear (); };
signals: signals:
void requestSendHeaders (void); void requestSendHeaders (void);
void requestSendData (void); void requestSendData (void);
private: private:
bool m_useChunked; bool m_useChunked;
StatusCode m_statusCode; StatusCode m_statusCode;
QByteArray m_data; QByteArray m_data;
QtHttpServer * m_serverHandle; QtHttpServer * m_serverHandle;
QHash<QByteArray, QByteArray> m_headersHash; QHash<QByteArray, QByteArray> m_headersHash;
}; };
#endif // QTHTTPREPLY_H #endif // QTHTTPREPLY_H

View File

@ -4,80 +4,31 @@
#include "QtHttpServer.h" #include "QtHttpServer.h"
QtHttpRequest::QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent) QtHttpRequest::QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent)
: QObject (parent) : QObject (parent)
, m_url (QUrl ()) , m_url (QUrl ())
, m_command (QString ()) , m_command (QString ())
, m_data (QByteArray ()) , m_data (QByteArray ())
, m_serverHandle (parent) , m_serverHandle (parent)
, m_clientHandle (client) , m_clientHandle (client)
, m_postData (QtHttpPostData()) , m_postData (QtHttpPostData())
{ {
// set some additional headers // set some additional headers
addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0")); addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0"));
addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive")); addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive"));
} }
QUrl QtHttpRequest::getUrl (void) const { void QtHttpRequest::setClientInfo (const QHostAddress & server, const QHostAddress & client)
return m_url; {
m_clientInfo.serverAddress = server;
m_clientInfo.clientAddress = client;
} }
QString QtHttpRequest::getCommand (void) const { void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value)
return m_command; {
} QByteArray key = header.trimmed ();
QtHttpRequest::ClientInfo QtHttpRequest::getClientInfo (void) const { if (!key.isEmpty ())
return m_clientInfo; {
} m_headersHash.insert (key, value);
}
int QtHttpRequest::getRawDataSize (void) const {
return m_data.size ();
}
QByteArray QtHttpRequest::getRawData (void) const {
return m_data;
}
QtHttpPostData QtHttpRequest::getPostData (void) const {
return m_postData;
}
QList<QByteArray> QtHttpRequest::getHeadersList (void) const {
return m_headersHash.keys ();
}
QtHttpClientWrapper * QtHttpRequest::getClient (void) const {
return m_clientHandle;
}
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::setClientInfo (const QHostAddress & server, const QHostAddress & client) {
m_clientInfo.serverAddress = server;
m_clientInfo.clientAddress = client;
}
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);
}
void QtHttpRequest::setPostData (const QtHttpPostData & data) {
m_postData = data;
} }

View File

@ -14,45 +14,50 @@ class QtHttpClientWrapper;
using QtHttpPostData = QMap<QString,QByteArray>; using QtHttpPostData = QMap<QString,QByteArray>;
class QtHttpRequest : public QObject { class QtHttpRequest : public QObject
Q_OBJECT {
Q_OBJECT
public: public:
explicit QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent); explicit QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent);
struct ClientInfo { struct ClientInfo
QHostAddress serverAddress; {
QHostAddress clientAddress; QHostAddress serverAddress;
}; QHostAddress clientAddress;
};
int getRawDataSize (void) const; int getRawDataSize (void) const { return m_data.size (); };
QUrl getUrl (void) const; QUrl getUrl (void) const { return m_url; };
QString getCommand (void) const; QString getCommand (void) const { return m_command; };
QByteArray getRawData (void) const; QByteArray getRawData (void) const { return m_data; };
QList<QByteArray> getHeadersList (void) const; QList<QByteArray> getHeadersList (void) const { return m_headersHash.keys (); };
QtHttpClientWrapper * getClient (void) const; QtHttpClientWrapper * getClient (void) const { return m_clientHandle; };
QtHttpPostData getPostData (void) const { return m_postData; };
ClientInfo getClientInfo (void) const { return m_clientInfo; };
QByteArray getHeader (const QByteArray & header) const; QByteArray getHeader (const QByteArray & header) const
QtHttpPostData getPostData (void) const; {
return m_headersHash.value (header, QByteArray ());
ClientInfo getClientInfo (void) const; };
public slots: public slots:
void setUrl (const QUrl & url); void setUrl (const QUrl & url) { m_url = url; };
void setCommand (const QString & command); void setCommand (const QString & command) { m_command = command; };
void setClientInfo (const QHostAddress & server, const QHostAddress & client); void appendRawData (const QByteArray & data) { m_data.append (data); };
void addHeader (const QByteArray & header, const QByteArray & value); void setPostData (const QtHttpPostData & data) { m_postData = data; };
void appendRawData (const QByteArray & data);
void setPostData (const QtHttpPostData & data); void setClientInfo (const QHostAddress & server, const QHostAddress & client);
void addHeader (const QByteArray & header, const QByteArray & value);
private: private:
QUrl m_url; QUrl m_url;
QString m_command; QString m_command;
QByteArray m_data; QByteArray m_data;
QtHttpServer * m_serverHandle; QtHttpServer * m_serverHandle;
QtHttpClientWrapper * m_clientHandle; QtHttpClientWrapper * m_clientHandle;
QHash<QByteArray, QByteArray> m_headersHash; QHash<QByteArray, QByteArray> m_headersHash;
ClientInfo m_clientInfo; ClientInfo m_clientInfo;
QtHttpPostData m_postData; QtHttpPostData m_postData;
}; };

View File

@ -11,142 +11,116 @@
const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1"); const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1");
QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent) QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent)
: QTcpServer (parent) : QTcpServer (parent)
, m_useSsl (false) , m_useSsl (false)
{ } {
QtHttpServerWrapper::~QtHttpServerWrapper (void) { }
void QtHttpServerWrapper::setUseSecure (const bool ssl) {
m_useSsl = ssl;
} }
void QtHttpServerWrapper::incomingConnection (qintptr handle) { QtHttpServerWrapper::~QtHttpServerWrapper (void)
QTcpSocket * sock = (m_useSsl {
? new QSslSocket (this)
: new QTcpSocket (this)); }
if (sock->setSocketDescriptor (handle)) {
addPendingConnection (sock); void QtHttpServerWrapper::setUseSecure (const bool ssl)
} {
else { m_useSsl = ssl;
delete sock; }
}
void QtHttpServerWrapper::incomingConnection (qintptr handle)
{
QTcpSocket * sock = (m_useSsl ? new QSslSocket (this) : new QTcpSocket (this));
sock->setSocketDescriptor(handle) ? addPendingConnection (sock) : delete sock;
} }
QtHttpServer::QtHttpServer (QObject * parent) QtHttpServer::QtHttpServer (QObject * parent)
: QObject (parent) : QObject (parent)
, m_useSsl (false) , m_useSsl (false)
, m_serverName (QStringLiteral ("The Qt5 HTTP Server")) , m_serverName (QStringLiteral ("The Qt5 HTTP Server"))
, m_netOrigin (NetOrigin::getInstance()) , m_netOrigin (NetOrigin::getInstance())
{ {
m_sockServer = new QtHttpServerWrapper (this); m_sockServer = new QtHttpServerWrapper (this);
connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected); connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected);
} }
const QString & QtHttpServer::getServerName (void) const { void QtHttpServer::start (quint16 port)
return m_serverName; {
}
quint16 QtHttpServer::getServerPort (void) const {
return m_sockServer->serverPort ();
}
QString QtHttpServer::getErrorString (void) const {
return m_sockServer->errorString ();
}
void QtHttpServer::start (quint16 port) {
if(!m_sockServer->isListening()) if(!m_sockServer->isListening())
{ {
if (m_sockServer->listen (QHostAddress::Any, port)) { m_sockServer->listen (QHostAddress::Any, port)
emit started (m_sockServer->serverPort ()); ? emit started (m_sockServer->serverPort ())
} : emit error (m_sockServer->errorString ());
else {
emit error (m_sockServer->errorString ());
}
} }
} }
void QtHttpServer::stop (void) { void QtHttpServer::stop (void)
if (m_sockServer->isListening ()) { {
m_sockServer->close (); if (m_sockServer->isListening ())
{
m_sockServer->close ();
// disconnect clients // disconnect clients
const QList<QTcpSocket*> socks = m_socksClientsHash.keys(); const QList<QTcpSocket*> socks = m_socksClientsHash.keys();
for(auto sock : socks) for(auto sock : socks)
{ {
sock->close(); sock->close();
} }
emit stopped ();
} emit stopped ();
}
} }
void QtHttpServer::setServerName (const QString & serverName) { void QtHttpServer::setUseSecure (const bool ssl)
m_serverName = serverName; {
m_useSsl = ssl;
m_sockServer->setUseSecure (m_useSsl);
} }
void QtHttpServer::setUseSecure (const bool ssl) { void QtHttpServer::onClientConnected (void)
m_useSsl = ssl; {
m_sockServer->setUseSecure (m_useSsl); while (m_sockServer->hasPendingConnections ())
} {
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ())
void QtHttpServer::setPrivateKey (const QSslKey & key) { {
m_sslKey = key;
}
void QtHttpServer::setCertificates (const QList<QSslCertificate> & certs) {
m_sslCerts = certs;
}
void QtHttpServer::onClientConnected (void) {
while (m_sockServer->hasPendingConnections ()) {
if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) {
if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress())) if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress()))
{ {
connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected); connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected);
if (m_useSsl) {
if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock)) { if (m_useSsl)
connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors); {
connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted); if (QSslSocket * ssl = qobject_cast<QSslSocket *> (sock))
connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError); {
connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged); connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors);
ssl->setLocalCertificateChain (m_sslCerts); connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted);
ssl->setPrivateKey (m_sslKey); connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError);
ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer); connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged);
ssl->startServerEncryption (); ssl->setLocalCertificateChain (m_sslCerts);
} ssl->setPrivateKey (m_sslKey);
} ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer);
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); ssl->startServerEncryption ();
m_socksClientsHash.insert (sock, wrapper); }
emit clientConnected (wrapper->getGuid ()); }
QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this);
m_socksClientsHash.insert (sock, wrapper);
emit clientConnected (wrapper->getGuid ());
} }
else else
{ {
sock->close(); sock->close();
} }
} }
} }
} }
void QtHttpServer::onClientSslEncrypted (void) { } void QtHttpServer::onClientDisconnected (void)
{
void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err) { if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ()))
Q_UNUSED (err) {
} if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR))
{
void QtHttpServer::onClientSslErrors (const QList<QSslError> & errors) { emit clientDisconnected (wrapper->getGuid ());
Q_UNUSED (errors) wrapper->deleteLater ();
} m_socksClientsHash.remove (sockClient);
}
void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode) { }
Q_UNUSED (mode)
}
void QtHttpServer::onClientDisconnected (void) {
if (QTcpSocket * sockClient = qobject_cast<QTcpSocket *> (sender ())) {
if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR)) {
emit clientDisconnected (wrapper->getGuid ());
wrapper->deleteLater ();
m_socksClientsHash.remove (sockClient);
}
}
} }

View File

@ -13,76 +13,77 @@
class QTcpSocket; class QTcpSocket;
class QTcpServer; class QTcpServer;
class QtHttpRequest; class QtHttpRequest;
class QtHttpReply; class QtHttpReply;
class QtHttpClientWrapper; class QtHttpClientWrapper;
class NetOrigin; class NetOrigin;
class QtHttpServerWrapper : public QTcpServer { class QtHttpServerWrapper : public QTcpServer
Q_OBJECT {
Q_OBJECT
public: public:
explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR); explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR);
virtual ~QtHttpServerWrapper (void); virtual ~QtHttpServerWrapper (void);
void setUseSecure (const bool ssl = true); void setUseSecure (const bool ssl = true);
protected: protected:
void incomingConnection (qintptr handle) Q_DECL_OVERRIDE; void incomingConnection (qintptr handle) Q_DECL_OVERRIDE;
private: private:
bool m_useSsl; bool m_useSsl;
}; };
class QtHttpServer : public QObject { class QtHttpServer : public QObject
Q_OBJECT {
Q_OBJECT
public: public:
explicit QtHttpServer (QObject * parent = Q_NULLPTR); explicit QtHttpServer (QObject * parent = Q_NULLPTR);
static const QString & HTTP_VERSION; static const QString & HTTP_VERSION;
typedef void (QSslSocket::* SslErrorSignal) (const QList<QSslError> &); typedef void (QSslSocket::* SslErrorSignal) (const QList<QSslError> &);
const QString & getServerName (void) const; const QString & getServerName (void) const { return m_serverName; };
quint16 getServerPort (void) const; quint16 getServerPort (void) const { return m_sockServer->serverPort(); };
QString getErrorString (void) const; QString getErrorString (void) const { return m_sockServer->errorString(); };
bool isListening() { return m_sockServer->isListening(); }; bool isListening() { return m_sockServer->isListening(); };
public slots: public slots:
void start (quint16 port = 0); void start (quint16 port = 0);
void stop (void); void stop (void);
void setServerName (const QString & serverName); void setUseSecure (const bool ssl = true);
void setUseSecure (const bool ssl = true); void setServerName (const QString & serverName) { m_serverName = serverName; };
void setPrivateKey (const QSslKey & key); void setPrivateKey (const QSslKey & key) { m_sslKey = key; };
void setCertificates (const QList<QSslCertificate> & certs); void setCertificates (const QList<QSslCertificate> & certs) { m_sslCerts = certs; };
signals: signals:
void started (quint16 port); void started (quint16 port);
void stopped (void); void stopped (void);
void error (const QString & msg); void error (const QString & msg);
void clientConnected (const QString & guid); void clientConnected (const QString & guid);
void clientDisconnected (const QString & guid); void clientDisconnected (const QString & guid);
void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply); void requestNeedsReply (QtHttpRequest * request, QtHttpReply * reply);
private slots: private slots:
void onClientConnected (void); void onClientConnected (void);
void onClientDisconnected (void); void onClientDisconnected (void);
void onClientSslEncrypted (void); void onClientSslEncrypted (void) { };
void onClientSslPeerVerifyError (const QSslError & err); void onClientSslPeerVerifyError (const QSslError & err) { Q_UNUSED (err) };
void onClientSslErrors (const QList<QSslError> & errors); void onClientSslErrors (const QList<QSslError> & errors) { Q_UNUSED (errors) };
void onClientSslModeChanged (QSslSocket::SslMode mode); void onClientSslModeChanged (QSslSocket::SslMode mode) { Q_UNUSED (mode) };
private: private:
bool m_useSsl; bool m_useSsl;
QSslKey m_sslKey; QSslKey m_sslKey;
QList<QSslCertificate> m_sslCerts; QList<QSslCertificate> m_sslCerts;
QString m_serverName; QString m_serverName;
NetOrigin* m_netOrigin; NetOrigin* m_netOrigin;
QtHttpServerWrapper * m_sockServer; QtHttpServerWrapper * m_sockServer;
QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash; QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash;
}; };
#endif // QTHTTPSERVER_H #endif // QTHTTPSERVER_H

View File

@ -63,7 +63,8 @@ void WebServer::onServerStarted (quint16 port)
emit stateChange(true); emit stateChange(true);
} }
void WebServer::onServerStopped () { void WebServer::onServerStopped ()
{
Info(_log, "Stopped %s", _server->getServerName().toStdString().c_str()); Info(_log, "Stopped %s", _server->getServerName().toStdString().c_str());
emit stateChange(false); emit stateChange(false);
} }

View File

@ -14,7 +14,6 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
: QObject(parent) : QObject(parent)
, _socket(sock) , _socket(sock)
, _log(Logger::getInstance("WEBSOCKET")) , _log(Logger::getInstance("WEBSOCKET"))
// , _hyperion(Hyperion::getInstance())
{ {
// connect socket; disconnect handled from QtHttpServer // connect socket; disconnect handled from QtHttpServer
connect(_socket, &QTcpSocket::readyRead , this, &WebSocketClient::handleWebSocketFrame); connect(_socket, &QTcpSocket::readyRead , this, &WebSocketClient::handleWebSocketFrame);
@ -45,107 +44,113 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const
void WebSocketClient::handleWebSocketFrame(void) void WebSocketClient::handleWebSocketFrame(void)
{ {
// we are on no continious reading from socket from call before while (_socket->bytesAvailable())
if (!_notEnoughData)
{ {
getWsFrameHeader(&_wsh); // we are on no continious reading from socket from call before
} if (!_notEnoughData)
if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength)
{
//printf("not enough data %llu %llu\n", _socket->bytesAvailable(), _wsh.payloadLength);
_notEnoughData=true;
return;
}
_notEnoughData = false;
QByteArray buf = _socket->read(_wsh.payloadLength);
//printf("opcode %x payload bytes %llu avail: %llu\n", _wsh.opCode, _wsh.payloadLength, _socket->bytesAvailable());
if (OPCODE::invalid((OPCODE::value)_wsh.opCode))
{
sendClose(CLOSECODE::INV_TYPE, "invalid opcode");
return;
}
// check the type of data frame
bool isContinuation=false;
switch (_wsh.opCode)
{
case OPCODE::CONTINUATION:
isContinuation = true;
// no break here, just jump over to opcode text
case OPCODE::BINARY:
case OPCODE::TEXT:
{ {
// check for protocal violations getWsFrameHeader(&_wsh);
if (_onContinuation && !isContinuation) }
{
sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames");
return;
}
if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT) if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength)
{ {
sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed"); //printf("not enough data %llu %llu\n", _socket->bytesAvailable(), _wsh.payloadLength);
return; _notEnoughData=true;
} return;
}
_notEnoughData = false;
// unmask data QByteArray buf = _socket->read(_wsh.payloadLength);
for (int i=0; i < buf.size(); i++) //printf("opcode %x payload bytes %llu avail: %llu\n", _wsh.opCode, _wsh.payloadLength, _socket->bytesAvailable());
{
buf[i] = buf[i] ^ _wsh.key[i % 4];
}
_onContinuation = !_wsh.fin || isContinuation; if (OPCODE::invalid((OPCODE::value)_wsh.opCode))
{
sendClose(CLOSECODE::INV_TYPE, "invalid opcode");
return;
}
// frame contains text, extract it, append data if this is a continuation // check the type of data frame
if (_wsh.fin && ! isContinuation) // one frame bool isContinuation=false;
{
_wsReceiveBuffer.clear();
}
_wsReceiveBuffer.append(buf);
// this is the final frame, decode and handle data switch (_wsh.opCode)
if (_wsh.fin) {
case OPCODE::CONTINUATION:
isContinuation = true;
// no break here, just jump over to opcode text
case OPCODE::BINARY:
case OPCODE::TEXT:
{ {
_onContinuation = false; // check for protocol violations
if (_onContinuation && !isContinuation)
{
sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames");
return;
}
if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT)
{
sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed");
return;
}
// unmask data
for (int i=0; i < buf.size(); i++)
{
buf[i] = buf[i] ^ _wsh.key[i % 4];
}
_onContinuation = !_wsh.fin || isContinuation;
// frame contains text, extract it, append data if this is a continuation
if (_wsh.fin && ! isContinuation) // one frame
{
_wsReceiveBuffer.clear();
}
_wsReceiveBuffer.append(buf);
// this is the final frame, decode and handle data
if (_wsh.fin)
{
_onContinuation = false;
if (_wsh.opCode == OPCODE::TEXT) if (_wsh.opCode == OPCODE::TEXT)
{ {
_jsonAPI->handleMessage(QString(_wsReceiveBuffer));
_jsonAPI->handleMessage(QString(_wsReceiveBuffer));
} }
else else
{ {
handleBinaryMessage(_wsReceiveBuffer); handleBinaryMessage(_wsReceiveBuffer);
} }
_wsReceiveBuffer.clear(); _wsReceiveBuffer.clear();
}
} }
break;
case OPCODE::CLOSE:
{
sendClose(CLOSECODE::NORMAL);
}
break;
case OPCODE::PING:
{
// ping received, send pong
quint8 pong[] = {OPCODE::PONG, 0};
_socket->write((const char*)pong, 2);
_socket->flush();
}
break;
case OPCODE::PONG:
{
Error(_log, "pong received, protocol violation!");
}
default:
Warning(_log, "strange %d\n%s\n", _wsh.opCode, QSTRING_CSTR(QString(buf)));
} }
break;
case OPCODE::CLOSE:
{
sendClose(CLOSECODE::NORMAL);
}
break;
case OPCODE::PING:
{
// ping received, send pong
quint8 pong[] = {OPCODE::PONG, 0};
_socket->write((const char*)pong, 2);
_socket->flush();
}
break;
case OPCODE::PONG:
{
Error(_log, "pong received, protocol violation!");
}
default:
Warning(_log, "strange %d\n%s\n", _wsh.opCode, QSTRING_CSTR(QString(buf)));
} }
} }
@ -221,6 +226,7 @@ void WebSocketClient::sendClose(int status, QString reason)
_socket->close(); _socket->close();
} }
void WebSocketClient::handleBinaryMessage(QByteArray &data) void WebSocketClient::handleBinaryMessage(QByteArray &data)
{ {
//uint8_t priority = data.at(0); //uint8_t priority = data.at(0);
@ -243,6 +249,7 @@ void WebSocketClient::handleBinaryMessage(QByteArray &data)
//_hyperion->setInputImage(priority, image, duration_s*1000); //_hyperion->setInputImage(priority, image, duration_s*1000);
} }
qint64 WebSocketClient::sendMessage(QJsonObject obj) qint64 WebSocketClient::sendMessage(QJsonObject obj)
{ {
QJsonDocument writer(obj); QJsonDocument writer(obj);

View File

@ -17,7 +17,7 @@ public:
struct WebSocketHeader struct WebSocketHeader
{ {
bool fin; bool fin;
quint8 opCode; quint8 opCode;
bool masked; bool masked;
quint64 payloadLength; quint64 payloadLength;
char key[4]; char key[4];

View File

@ -4,8 +4,10 @@
/** /**
* WebSocket Opcodes are 4 bits. See RFC6455 section 5.2. * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
*/ */
namespace OPCODE { namespace OPCODE
enum value { {
enum value
{
CONTINUATION = 0x0, CONTINUATION = 0x0,
TEXT = 0x1, TEXT = 0x1,
BINARY = 0x2, BINARY = 0x2,
@ -29,7 +31,8 @@ namespace OPCODE {
* @param v The opcode to test. * @param v The opcode to test.
* @return Whether or not the opcode is reserved. * @return Whether or not the opcode is reserved.
*/ */
inline bool reserved(value v) { inline bool reserved(value v)
{
return (v >= RSV3 && v <= RSV7) || (v >= CONTROL_RSVB && v <= CONTROL_RSVF); return (v >= RSV3 && v <= RSV7) || (v >= CONTROL_RSVB && v <= CONTROL_RSVF);
} }
@ -40,7 +43,8 @@ namespace OPCODE {
* @param v The opcode to test. * @param v The opcode to test.
* @return Whether or not the opcode is invalid. * @return Whether or not the opcode is invalid.
*/ */
inline bool invalid(value v) { inline bool invalid(value v)
{
return (v > 0xF || v < 0); return (v > 0xF || v < 0);
} }
@ -49,13 +53,16 @@ namespace OPCODE {
* @param v The opcode to test. * @param v The opcode to test.
* @return Whether or not the opcode is a control opcode. * @return Whether or not the opcode is a control opcode.
*/ */
inline bool is_control(value v) { inline bool is_control(value v)
{
return v >= 0x8; return v >= 0x8;
} }
} }
namespace CLOSECODE { namespace CLOSECODE
enum value { {
enum value
{
NORMAL = 1000, NORMAL = 1000,
AWAY = 1001, AWAY = 1001,
TERM = 1002, TERM = 1002,