diff --git a/assets/webconfig/js/content_remote.js b/assets/webconfig/js/content_remote.js index 21df03fd..2138ce1b 100644 --- a/assets/webconfig/js/content_remote.js +++ b/assets/webconfig/js/content_remote.js @@ -298,7 +298,7 @@ $(document).ready(function() { createCP('cp2', cpcolor, function(rgbT,hex){ rgb = rgbT; - sendColor() + sendColor(); setStorage('rmcpcolor', hex); }); diff --git a/libsrc/hyperion/LinearColorSmoothing.cpp b/libsrc/hyperion/LinearColorSmoothing.cpp index 6bef0d58..d49e0c4b 100644 --- a/libsrc/hyperion/LinearColorSmoothing.cpp +++ b/libsrc/hyperion/LinearColorSmoothing.cpp @@ -180,8 +180,6 @@ void LinearColorSmoothing::componentStateChange(const hyperion::Components compo void LinearColorSmoothing::setEnable(bool enable) { - LedDevice::setEnable(enable); - if (!enable) { _timer->stop(); diff --git a/libsrc/webserver/QtHttpClientWrapper.cpp b/libsrc/webserver/QtHttpClientWrapper.cpp index 3ce9a368..41cf5403 100644 --- a/libsrc/webserver/QtHttpClientWrapper.cpp +++ b/libsrc/webserver/QtHttpClientWrapper.cpp @@ -27,93 +27,131 @@ QtHttpClientWrapper::QtHttpClientWrapper (QTcpSocket * sock, const bool& localCo , m_websocketClient(nullptr) , m_webJsonRpc (nullptr) { - connect (m_sockClient, &QTcpSocket::readyRead, this, &QtHttpClientWrapper::onClientDataReceived); + 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; +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) { - m_currentRequest = new QtHttpRequest (this, m_serverHandle); - m_currentRequest->setClientInfo(m_sockClient->localAddress(), m_sockClient->peerAddress()); - 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 (); - m_currentRequest->addHeader (header, value); - if (header == QtHttpHeader::ContentLength) { - bool ok = false; - const int 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 - 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 +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) + { + m_currentRequest = new QtHttpRequest (this, m_serverHandle); + m_currentRequest->setClientInfo(m_sockClient->localAddress(), m_sockClient->peerAddress()); + 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 (); + m_currentRequest->addHeader (header, value); + if (header == QtHttpHeader::ContentLength) + { + bool ok = false; + const int 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 + { + 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" if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket") { @@ -121,146 +159,184 @@ void QtHttpClientWrapper::onClientDataReceived (void) { { // disconnect this slot from socket for further requests 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); } + break; } + // add post data to request and catch /jsonrpc subroute url - if ( m_currentRequest->getCommand() == "POST") - { - QtHttpPostData postData; - QByteArray data = m_currentRequest->getRawData(); - QList parts = data.split('&'); - for (int i = 0; i < parts.size(); ++i) - { - QList keyValue = parts.at(i).split('='); - QByteArray value; - if (keyValue.size()>1) - { - value = QByteArray::fromPercentEncoding(keyValue.at(1)); - } - postData.insert(QString::fromUtf8(keyValue.at(0)),value); - } - m_currentRequest->setPostData(postData); + if ( m_currentRequest->getCommand() == "POST") + { + QtHttpPostData postData; + QByteArray data = m_currentRequest->getRawData(); + QList parts = data.split('&'); + + for (int i = 0; i < parts.size(); ++i) + { + QList keyValue = parts.at(i).split('='); + QByteArray value; + + if (keyValue.size()>1) + { + value = QByteArray::fromPercentEncoding(keyValue.at(1)); + } + + postData.insert(QString::fromUtf8(keyValue.at(0)),value); + } + + m_currentRequest->setPostData(postData); // catch /jsonrpc in url, we need async callback, StaticFileServing is sync QString path = m_currentRequest->getUrl ().path (); QStringList uri_parts = path.split('/', QString::SkipEmptyParts); + if ( ! uri_parts.empty() && uri_parts.at(0) == "json-rpc" ) { if(m_webJsonRpc == Q_NULLPTR) { m_webJsonRpc = new WebJsonRpc(m_currentRequest, m_serverHandle, m_localConnection, this); } + m_webJsonRpc->handleMessage(m_currentRequest); break; } - } + } - 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 + 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 ("

Bad Request (HTTP parsing error) !

")); - reply.appendRawData (CRLF); - m_parsingStatus = sendReplyToClient (&reply); - break; - } - default: { break; } - } - } - } + + 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 ("

Bad Request (HTTP parsing error) !

")); + reply.appendRawData (CRLF); + m_parsingStatus = sendReplyToClient (&reply); + + break; + } + default: + { + break; + } + } + } + } } -void QtHttpClientWrapper::onReplySendHeadersRequested (void) { - QtHttpReply * reply = qobject_cast (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 & 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::onReplySendHeadersRequested (void) +{ + QtHttpReply * reply = qobject_cast (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); + + if (reply->useChunked ()) // Header name: header value + { + static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked"); + reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED); + } + else + { + reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ())); + } + + const QList & 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 (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 (); - } +void QtHttpClientWrapper::onReplySendDataRequested (void) +{ + QtHttpReply * reply = qobject_cast (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 (); + } } -void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply) { - connect (reply, &QtHttpReply::requestSendHeaders, - this, &QtHttpClientWrapper::onReplySendHeadersRequested); - connect (reply, &QtHttpReply::requestSendData, - this, &QtHttpClientWrapper::onReplySendDataRequested); +void QtHttpClientWrapper::sendToClientWithReply(QtHttpReply * reply) +{ + connect (reply, &QtHttpReply::requestSendHeaders, this, &QtHttpClientWrapper::onReplySendHeadersRequested); + connect (reply, &QtHttpReply::requestSendData, this, &QtHttpClientWrapper::onReplySendDataRequested); m_parsingStatus = sendReplyToClient (reply); } -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; +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; } diff --git a/libsrc/webserver/QtHttpHeader.h b/libsrc/webserver/QtHttpHeader.h index e04ee457..9e2a85e9 100644 --- a/libsrc/webserver/QtHttpHeader.h +++ b/libsrc/webserver/QtHttpHeader.h @@ -3,35 +3,36 @@ class QByteArray; -class QtHttpHeader { +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; + 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; static const QByteArray & AccessControlAllow; // Websocket specific headers static const QByteArray & Upgrade; diff --git a/libsrc/webserver/QtHttpReply.cpp b/libsrc/webserver/QtHttpReply.cpp index f33e398c..95afb97d 100644 --- a/libsrc/webserver/QtHttpReply.cpp +++ b/libsrc/webserver/QtHttpReply.cpp @@ -6,70 +6,35 @@ #include QtHttpReply::QtHttpReply (QtHttpServer * parent) - : QObject (parent) - , m_useChunked (false) - , m_statusCode (Ok) - , m_data (QByteArray ()) - , m_serverHandle (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 ()); + // 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 (); +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 (""); + } } -bool QtHttpReply::useChunked (void) const { - return m_useChunked; -} +void QtHttpReply::addHeader (const QByteArray & header, const QByteArray & value) +{ + QByteArray key = header.trimmed (); -QtHttpReply::StatusCode QtHttpReply::getStatusCode (void) const { - return m_statusCode; -} - -QByteArray QtHttpReply::getRawData (void) const { - return m_data; -} - -QList 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 (); + if (!key.isEmpty ()) + { + m_headersHash.insert (key, value); + } } diff --git a/libsrc/webserver/QtHttpReply.h b/libsrc/webserver/QtHttpReply.h index 50993ac6..4f60a910 100644 --- a/libsrc/webserver/QtHttpReply.h +++ b/libsrc/webserver/QtHttpReply.h @@ -8,53 +8,58 @@ class QtHttpServer; -class QtHttpReply : public QObject { - Q_OBJECT - Q_ENUMS (StatusCode) +class QtHttpReply : public QObject +{ + Q_OBJECT + Q_ENUMS (StatusCode) public: - explicit QtHttpReply (QtHttpServer * parent); + explicit QtHttpReply (QtHttpServer * parent); - enum StatusCode { - Ok = 200, - SeeOther = 303, - BadRequest = 400, - Forbidden = 403, - NotFound = 404, - MethodNotAllowed = 405, - InternalError = 500, - NotImplemented = 501, - BadGateway = 502, - ServiceUnavailable = 503, - }; + enum StatusCode + { + Ok = 200, + SeeOther = 303, + BadRequest = 400, + Forbidden = 403, + NotFound = 404, + MethodNotAllowed = 405, + InternalError = 500, + NotImplemented = 501, + BadGateway = 502, + ServiceUnavailable = 503, + }; - int getRawDataSize (void) const; - bool useChunked (void) const; - StatusCode getStatusCode (void) const; - QByteArray getRawData (void) const; - QList getHeadersList (void) const; + int getRawDataSize (void) const { return m_data.size(); }; + bool useChunked (void) const { return m_useChunked; }; + StatusCode getStatusCode (void) const { return m_statusCode; }; + QByteArray getRawData (void) const { return m_data; }; + QList 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: - 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); + void setUseChunked (bool chunked = false) { m_useChunked = chunked; }; + void setStatusCode (StatusCode statusCode) { m_statusCode = statusCode; }; + void appendRawData (const QByteArray & data) { m_data.append(data); }; + void addHeader (const QByteArray & header, const QByteArray & value); + void resetRawData (void) { m_data.clear (); }; signals: - void requestSendHeaders (void); - void requestSendData (void); + void requestSendHeaders (void); + void requestSendData (void); private: - bool m_useChunked; - StatusCode m_statusCode; - QByteArray m_data; - QtHttpServer * m_serverHandle; - QHash m_headersHash; + bool m_useChunked; + StatusCode m_statusCode; + QByteArray m_data; + QtHttpServer * m_serverHandle; + QHash m_headersHash; }; #endif // QTHTTPREPLY_H diff --git a/libsrc/webserver/QtHttpRequest.cpp b/libsrc/webserver/QtHttpRequest.cpp index bb39e3cd..0adc8c4e 100644 --- a/libsrc/webserver/QtHttpRequest.cpp +++ b/libsrc/webserver/QtHttpRequest.cpp @@ -4,80 +4,31 @@ #include "QtHttpServer.h" QtHttpRequest::QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent) - : QObject (parent) - , m_url (QUrl ()) - , m_command (QString ()) - , m_data (QByteArray ()) - , m_serverHandle (parent) - , m_clientHandle (client) - , m_postData (QtHttpPostData()) + : QObject (parent) + , m_url (QUrl ()) + , m_command (QString ()) + , m_data (QByteArray ()) + , m_serverHandle (parent) + , m_clientHandle (client) + , m_postData (QtHttpPostData()) { - // set some additional headers - addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0")); - addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive")); + // set some additional headers + addHeader (QtHttpHeader::ContentLength, QByteArrayLiteral ("0")); + addHeader (QtHttpHeader::Connection, QByteArrayLiteral ("Keep-Alive")); } -QUrl QtHttpRequest::getUrl (void) const { - return m_url; +void QtHttpRequest::setClientInfo (const QHostAddress & server, const QHostAddress & client) +{ + m_clientInfo.serverAddress = server; + m_clientInfo.clientAddress = client; } -QString QtHttpRequest::getCommand (void) const { - return m_command; -} +void QtHttpRequest::addHeader (const QByteArray & header, const QByteArray & value) +{ + QByteArray key = header.trimmed (); -QtHttpRequest::ClientInfo QtHttpRequest::getClientInfo (void) const { - return m_clientInfo; -} - -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 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; + if (!key.isEmpty ()) + { + m_headersHash.insert (key, value); + } } diff --git a/libsrc/webserver/QtHttpRequest.h b/libsrc/webserver/QtHttpRequest.h index 52e175a3..df4ad2d2 100644 --- a/libsrc/webserver/QtHttpRequest.h +++ b/libsrc/webserver/QtHttpRequest.h @@ -14,45 +14,50 @@ class QtHttpClientWrapper; using QtHttpPostData = QMap; -class QtHttpRequest : public QObject { - Q_OBJECT +class QtHttpRequest : public QObject +{ + Q_OBJECT public: - explicit QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent); + explicit QtHttpRequest (QtHttpClientWrapper * client, QtHttpServer * parent); - struct ClientInfo { - QHostAddress serverAddress; - QHostAddress clientAddress; - }; + struct ClientInfo + { + QHostAddress serverAddress; + QHostAddress clientAddress; + }; - int getRawDataSize (void) const; - QUrl getUrl (void) const; - QString getCommand (void) const; - QByteArray getRawData (void) const; - QList getHeadersList (void) const; - QtHttpClientWrapper * getClient (void) const; + int getRawDataSize (void) const { return m_data.size (); }; + QUrl getUrl (void) const { return m_url; }; + QString getCommand (void) const { return m_command; }; + QByteArray getRawData (void) const { return m_data; }; + QList getHeadersList (void) const { return m_headersHash.keys (); }; + 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; - QtHttpPostData getPostData (void) const; - - ClientInfo getClientInfo (void) const; + QByteArray getHeader (const QByteArray & header) const + { + return m_headersHash.value (header, QByteArray ()); + }; public slots: - void setUrl (const QUrl & url); - void setCommand (const QString & command); - void setClientInfo (const QHostAddress & server, const QHostAddress & client); - void addHeader (const QByteArray & header, const QByteArray & value); - void appendRawData (const QByteArray & data); - void setPostData (const QtHttpPostData & data); + void setUrl (const QUrl & url) { m_url = url; }; + void setCommand (const QString & command) { m_command = command; }; + void appendRawData (const QByteArray & data) { m_data.append (data); }; + void setPostData (const QtHttpPostData & data) { m_postData = data; }; + + void setClientInfo (const QHostAddress & server, const QHostAddress & client); + void addHeader (const QByteArray & header, const QByteArray & value); private: - QUrl m_url; - QString m_command; - QByteArray m_data; - QtHttpServer * m_serverHandle; - QtHttpClientWrapper * m_clientHandle; - QHash m_headersHash; - ClientInfo m_clientInfo; + QUrl m_url; + QString m_command; + QByteArray m_data; + QtHttpServer * m_serverHandle; + QtHttpClientWrapper * m_clientHandle; + QHash m_headersHash; + ClientInfo m_clientInfo; QtHttpPostData m_postData; }; diff --git a/libsrc/webserver/QtHttpServer.cpp b/libsrc/webserver/QtHttpServer.cpp index 48661643..f038e391 100644 --- a/libsrc/webserver/QtHttpServer.cpp +++ b/libsrc/webserver/QtHttpServer.cpp @@ -11,142 +11,116 @@ const QString & QtHttpServer::HTTP_VERSION = QStringLiteral ("HTTP/1.1"); QtHttpServerWrapper::QtHttpServerWrapper (QObject * parent) - : QTcpServer (parent) - , m_useSsl (false) -{ } + : QTcpServer (parent) + , m_useSsl (false) +{ -QtHttpServerWrapper::~QtHttpServerWrapper (void) { } - -void QtHttpServerWrapper::setUseSecure (const bool ssl) { - m_useSsl = ssl; } -void QtHttpServerWrapper::incomingConnection (qintptr handle) { - QTcpSocket * sock = (m_useSsl - ? new QSslSocket (this) - : new QTcpSocket (this)); - if (sock->setSocketDescriptor (handle)) { - addPendingConnection (sock); - } - else { - delete sock; - } +QtHttpServerWrapper::~QtHttpServerWrapper (void) +{ + +} + +void QtHttpServerWrapper::setUseSecure (const bool ssl) +{ + m_useSsl = ssl; +} + +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) - : QObject (parent) - , m_useSsl (false) - , m_serverName (QStringLiteral ("The Qt5 HTTP Server")) + : QObject (parent) + , m_useSsl (false) + , m_serverName (QStringLiteral ("The Qt5 HTTP Server")) , m_netOrigin (NetOrigin::getInstance()) { - m_sockServer = new QtHttpServerWrapper (this); - connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected); + m_sockServer = new QtHttpServerWrapper (this); + connect (m_sockServer, &QtHttpServerWrapper::newConnection, this, &QtHttpServer::onClientConnected); } -const QString & QtHttpServer::getServerName (void) const { - 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) { +void QtHttpServer::start (quint16 port) +{ if(!m_sockServer->isListening()) { - if (m_sockServer->listen (QHostAddress::Any, port)) { - emit started (m_sockServer->serverPort ()); - } - else { - emit error (m_sockServer->errorString ()); - } + m_sockServer->listen (QHostAddress::Any, port) + ? emit started (m_sockServer->serverPort ()) + : emit error (m_sockServer->errorString ()); } } -void QtHttpServer::stop (void) { - if (m_sockServer->isListening ()) { - m_sockServer->close (); +void QtHttpServer::stop (void) +{ + if (m_sockServer->isListening ()) + { + m_sockServer->close (); // disconnect clients const QList socks = m_socksClientsHash.keys(); for(auto sock : socks) { sock->close(); } - emit stopped (); - } + + emit stopped (); + } } -void QtHttpServer::setServerName (const QString & serverName) { - m_serverName = serverName; +void QtHttpServer::setUseSecure (const bool ssl) +{ + m_useSsl = ssl; + m_sockServer->setUseSecure (m_useSsl); } -void QtHttpServer::setUseSecure (const bool ssl) { - m_useSsl = ssl; - m_sockServer->setUseSecure (m_useSsl); -} - -void QtHttpServer::setPrivateKey (const QSslKey & key) { - m_sslKey = key; -} - -void QtHttpServer::setCertificates (const QList & certs) { - m_sslCerts = certs; -} - -void QtHttpServer::onClientConnected (void) { - while (m_sockServer->hasPendingConnections ()) { - if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) { +void QtHttpServer::onClientConnected (void) +{ + while (m_sockServer->hasPendingConnections ()) + { + if (QTcpSocket * sock = m_sockServer->nextPendingConnection ()) + { if(m_netOrigin->accessAllowed(sock->peerAddress(), sock->localAddress())) { - connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected); - if (m_useSsl) { - if (QSslSocket * ssl = qobject_cast (sock)) { - connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors); - connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted); - connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError); - connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged); - ssl->setLocalCertificateChain (m_sslCerts); - ssl->setPrivateKey (m_sslKey); - ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer); - ssl->startServerEncryption (); - } - } - QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); - m_socksClientsHash.insert (sock, wrapper); - emit clientConnected (wrapper->getGuid ()); + connect (sock, &QTcpSocket::disconnected, this, &QtHttpServer::onClientDisconnected); + + if (m_useSsl) + { + if (QSslSocket * ssl = qobject_cast (sock)) + { + connect (ssl, SslErrorSignal (&QSslSocket::sslErrors), this, &QtHttpServer::onClientSslErrors); + connect (ssl, &QSslSocket::encrypted, this, &QtHttpServer::onClientSslEncrypted); + connect (ssl, &QSslSocket::peerVerifyError, this, &QtHttpServer::onClientSslPeerVerifyError); + connect (ssl, &QSslSocket::modeChanged, this, &QtHttpServer::onClientSslModeChanged); + ssl->setLocalCertificateChain (m_sslCerts); + ssl->setPrivateKey (m_sslKey); + ssl->setPeerVerifyMode (QSslSocket::AutoVerifyPeer); + ssl->startServerEncryption (); + } + } + + QtHttpClientWrapper * wrapper = new QtHttpClientWrapper (sock, m_netOrigin->isLocalAddress(sock->peerAddress(), sock->localAddress()), this); + m_socksClientsHash.insert (sock, wrapper); + emit clientConnected (wrapper->getGuid ()); } else { sock->close(); } - } - } + } + } } -void QtHttpServer::onClientSslEncrypted (void) { } - -void QtHttpServer::onClientSslPeerVerifyError (const QSslError & err) { - Q_UNUSED (err) -} - -void QtHttpServer::onClientSslErrors (const QList & errors) { - Q_UNUSED (errors) -} - -void QtHttpServer::onClientSslModeChanged (QSslSocket::SslMode mode) { - Q_UNUSED (mode) -} - -void QtHttpServer::onClientDisconnected (void) { - if (QTcpSocket * sockClient = qobject_cast (sender ())) { - if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR)) { - emit clientDisconnected (wrapper->getGuid ()); - wrapper->deleteLater (); - m_socksClientsHash.remove (sockClient); - } - } +void QtHttpServer::onClientDisconnected (void) +{ + if (QTcpSocket * sockClient = qobject_cast (sender ())) + { + if (QtHttpClientWrapper * wrapper = m_socksClientsHash.value (sockClient, Q_NULLPTR)) + { + emit clientDisconnected (wrapper->getGuid ()); + wrapper->deleteLater (); + m_socksClientsHash.remove (sockClient); + } + } } diff --git a/libsrc/webserver/QtHttpServer.h b/libsrc/webserver/QtHttpServer.h index 225e29d5..4c97cb15 100644 --- a/libsrc/webserver/QtHttpServer.h +++ b/libsrc/webserver/QtHttpServer.h @@ -13,76 +13,77 @@ class QTcpSocket; class QTcpServer; - class QtHttpRequest; class QtHttpReply; class QtHttpClientWrapper; class NetOrigin; -class QtHttpServerWrapper : public QTcpServer { - Q_OBJECT +class QtHttpServerWrapper : public QTcpServer +{ + Q_OBJECT public: - explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR); - virtual ~QtHttpServerWrapper (void); + explicit QtHttpServerWrapper (QObject * parent = Q_NULLPTR); + virtual ~QtHttpServerWrapper (void); - void setUseSecure (const bool ssl = true); + void setUseSecure (const bool ssl = true); protected: - void incomingConnection (qintptr handle) Q_DECL_OVERRIDE; + void incomingConnection (qintptr handle) Q_DECL_OVERRIDE; private: - bool m_useSsl; + bool m_useSsl; }; -class QtHttpServer : public QObject { - Q_OBJECT +class QtHttpServer : public QObject +{ + Q_OBJECT 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 &); + typedef void (QSslSocket::* SslErrorSignal) (const QList &); - const QString & getServerName (void) const; + const QString & getServerName (void) const { return m_serverName; }; - quint16 getServerPort (void) const; - QString getErrorString (void) const; - bool isListening() { return m_sockServer->isListening(); }; + quint16 getServerPort (void) const { return m_sockServer->serverPort(); }; + QString getErrorString (void) const { return m_sockServer->errorString(); }; + bool isListening() { return m_sockServer->isListening(); }; public slots: - void start (quint16 port = 0); - void stop (void); - void setServerName (const QString & serverName); - void setUseSecure (const bool ssl = true); - void setPrivateKey (const QSslKey & key); - void setCertificates (const QList & certs); + void start (quint16 port = 0); + void stop (void); + void setUseSecure (const bool ssl = true); + void setServerName (const QString & serverName) { m_serverName = serverName; }; + void setPrivateKey (const QSslKey & key) { m_sslKey = key; }; + void setCertificates (const QList & certs) { m_sslCerts = certs; }; 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); + 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); - void onClientSslEncrypted (void); - void onClientSslPeerVerifyError (const QSslError & err); - void onClientSslErrors (const QList & errors); - void onClientSslModeChanged (QSslSocket::SslMode mode); + void onClientConnected (void); + void onClientDisconnected (void); + void onClientSslEncrypted (void) { }; + void onClientSslPeerVerifyError (const QSslError & err) { Q_UNUSED (err) }; + void onClientSslErrors (const QList & errors) { Q_UNUSED (errors) }; + void onClientSslModeChanged (QSslSocket::SslMode mode) { Q_UNUSED (mode) }; private: - bool m_useSsl; - QSslKey m_sslKey; - QList m_sslCerts; - QString m_serverName; + bool m_useSsl; + QSslKey m_sslKey; + QList m_sslCerts; + QString m_serverName; NetOrigin* m_netOrigin; - QtHttpServerWrapper * m_sockServer; - QHash m_socksClientsHash; + QtHttpServerWrapper * m_sockServer; + QHash m_socksClientsHash; }; #endif // QTHTTPSERVER_H diff --git a/libsrc/webserver/WebServer.cpp b/libsrc/webserver/WebServer.cpp index 876a025c..723ec136 100644 --- a/libsrc/webserver/WebServer.cpp +++ b/libsrc/webserver/WebServer.cpp @@ -63,7 +63,8 @@ void WebServer::onServerStarted (quint16 port) emit stateChange(true); } -void WebServer::onServerStopped () { +void WebServer::onServerStopped () +{ Info(_log, "Stopped %s", _server->getServerName().toStdString().c_str()); emit stateChange(false); } diff --git a/libsrc/webserver/WebSocketClient.cpp b/libsrc/webserver/WebSocketClient.cpp index d7ed480b..a16ecf1e 100644 --- a/libsrc/webserver/WebSocketClient.cpp +++ b/libsrc/webserver/WebSocketClient.cpp @@ -14,7 +14,6 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const : QObject(parent) , _socket(sock) , _log(Logger::getInstance("WEBSOCKET")) -// , _hyperion(Hyperion::getInstance()) { // connect socket; disconnect handled from QtHttpServer connect(_socket, &QTcpSocket::readyRead , this, &WebSocketClient::handleWebSocketFrame); @@ -45,107 +44,113 @@ WebSocketClient::WebSocketClient(QtHttpRequest* request, QTcpSocket* sock, const void WebSocketClient::handleWebSocketFrame(void) { - // we are on no continious reading from socket from call before - if (!_notEnoughData) + while (_socket->bytesAvailable()) { - getWsFrameHeader(&_wsh); - } - - 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: + // we are on no continious reading from socket from call before + if (!_notEnoughData) { - // check for protocal violations - if (_onContinuation && !isContinuation) - { - sendClose(CLOSECODE::VIOLATION, "protocol violation, somebody sends frames in between continued frames"); - return; - } + getWsFrameHeader(&_wsh); + } - if (!_wsh.masked && _wsh.opCode == OPCODE::TEXT) - { - sendClose(CLOSECODE::VIOLATION, "protocol violation, unmasked text frames not allowed"); - return; - } + if(_socket->bytesAvailable() < (qint64)_wsh.payloadLength) + { + //printf("not enough data %llu %llu\n", _socket->bytesAvailable(), _wsh.payloadLength); + _notEnoughData=true; + return; + } + _notEnoughData = false; - // unmask data - for (int i=0; i < buf.size(); i++) - { - buf[i] = buf[i] ^ _wsh.key[i % 4]; - } + QByteArray buf = _socket->read(_wsh.payloadLength); + //printf("opcode %x payload bytes %llu avail: %llu\n", _wsh.opCode, _wsh.payloadLength, _socket->bytesAvailable()); - _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 - if (_wsh.fin && ! isContinuation) // one frame - { - _wsReceiveBuffer.clear(); - } - _wsReceiveBuffer.append(buf); + // check the type of data frame + bool isContinuation=false; - // this is the final frame, decode and handle data - if (_wsh.fin) + switch (_wsh.opCode) + { + 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) { - _jsonAPI->handleMessage(QString(_wsReceiveBuffer)); + + _jsonAPI->handleMessage(QString(_wsReceiveBuffer)); } else { 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(); } + void WebSocketClient::handleBinaryMessage(QByteArray &data) { //uint8_t priority = data.at(0); @@ -243,6 +249,7 @@ void WebSocketClient::handleBinaryMessage(QByteArray &data) //_hyperion->setInputImage(priority, image, duration_s*1000); } + qint64 WebSocketClient::sendMessage(QJsonObject obj) { QJsonDocument writer(obj); diff --git a/libsrc/webserver/WebSocketClient.h b/libsrc/webserver/WebSocketClient.h index 80e1491b..137cadbd 100644 --- a/libsrc/webserver/WebSocketClient.h +++ b/libsrc/webserver/WebSocketClient.h @@ -17,7 +17,7 @@ public: struct WebSocketHeader { bool fin; - quint8 opCode; + quint8 opCode; bool masked; quint64 payloadLength; char key[4]; diff --git a/libsrc/webserver/WebSocketUtils.h b/libsrc/webserver/WebSocketUtils.h index 271431f5..f490ae97 100644 --- a/libsrc/webserver/WebSocketUtils.h +++ b/libsrc/webserver/WebSocketUtils.h @@ -4,8 +4,10 @@ /** * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2. */ -namespace OPCODE { - enum value { +namespace OPCODE +{ + enum value + { CONTINUATION = 0x0, TEXT = 0x1, BINARY = 0x2, @@ -29,7 +31,8 @@ namespace OPCODE { * @param v The opcode to test. * @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); } @@ -40,7 +43,8 @@ namespace OPCODE { * @param v The opcode to test. * @return Whether or not the opcode is invalid. */ - inline bool invalid(value v) { + inline bool invalid(value v) + { return (v > 0xF || v < 0); } @@ -49,13 +53,16 @@ namespace OPCODE { * @param v The opcode to test. * @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; } } -namespace CLOSECODE { - enum value { +namespace CLOSECODE +{ + enum value + { NORMAL = 1000, AWAY = 1001, TERM = 1002,