diff --git a/libsrc/webserver/QtHttpClientWrapper.cpp b/libsrc/webserver/QtHttpClientWrapper.cpp index 4ea3bd83..ea9aeb0d 100644 --- a/libsrc/webserver/QtHttpClientWrapper.cpp +++ b/libsrc/webserver/QtHttpClientWrapper.cpp @@ -37,7 +37,7 @@ QString QtHttpClientWrapper::getGuid (void) { m_guid = QString::fromLocal8Bit ( QCryptographicHash::hash ( - QByteArray::number ((quint64) (this)), + QByteArray::number (reinterpret_cast(this)), QCryptographicHash::Md5 ).toHex () ); @@ -50,66 +50,70 @@ void QtHttpClientWrapper::onClientDataReceived (void) { if (m_sockClient != Q_NULLPTR) { - while (m_sockClient->bytesAvailable ()) + while (m_sockClient->bytesAvailable () != 0) { QByteArray line = m_sockClient->readLine (); switch (m_parsingStatus) // handle parsing steps { - case AwaitingRequest: // "command url version" × 1 + case AwaitingRequest: // "command url version" × 1 + { + QString str = QString::fromUtf8 (line).trimmed (); + QStringList parts = QStringUtils::split(str,SPACE, QStringUtils::SplitBehavior::SkipEmptyParts); + if (parts.size () == 3) { - QString str = QString::fromUtf8 (line).trimmed (); - QStringList parts = QStringUtils::split(str,SPACE, QStringUtils::SplitBehavior::SkipEmptyParts); - if (parts.size () == 3) - { - QString command = parts.at (0); - QString url = parts.at (1); - QString version = parts.at (2); + const QString& command = parts.at (0); + const QString& url = parts.at (1); + const 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; - // Error : unhandled HTTP version - } + 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; - // Error : incorrect HTTP command line + // Error : unhandled HTTP version } - - break; } - case AwaitingHeaders: // "header: value" × N (until empty line) + else { - QByteArray raw = line.trimmed (); + m_parsingStatus = ParsingError; + // Error : incorrect HTTP command line + } + m_fragment.clear(); + break; + } + case AwaitingHeaders: // "header: value" × N (until empty line) + { + m_fragment.append(line); + + if ( m_fragment.endsWith(CRLF)) + { + QByteArray raw = m_fragment.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 (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) if (header.compare(QtHttpHeader::ContentLength, Qt::CaseInsensitive) == 0) - #else +#else if (header.toLower() == QtHttpHeader::ContentLength.toLower()) - #endif +#endif { - bool ok = false; - const int len = value.toInt (&ok, 10); - if (ok) + bool isConversionOk = false; + const int len = value.toInt (&isConversionOk, 10); + if (isConversionOk) { m_currentRequest->addHeader (QtHttpHeader::ContentLength, QByteArray::number (len)); } @@ -132,107 +136,109 @@ void QtHttpClientWrapper::onClientDataReceived (void) m_parsingStatus = RequestParsed; } } - - break; + m_fragment.clear(); } - case AwaitingContent: // raw data × N (until EOF ??) + + break; + } + case AwaitingContent: // raw data × N (until EOF ??) + { + m_currentRequest->appendRawData (line); + + if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ()) { - m_currentRequest->appendRawData (line); - - if (m_currentRequest->getRawDataSize () == m_currentRequest->getHeader (QtHttpHeader::ContentLength).toInt ()) - { - m_parsingStatus = RequestParsed; - } - - break; - } - default: - { - break; + m_parsingStatus = RequestParsed; } + + break; + } + default: + { + break; + } } switch (m_parsingStatus) // handle parsing status end/error { - case RequestParsed: // a valid request has ben fully parsed + case RequestParsed: // a valid request has ben fully parsed + { + // Catch websocket header "Upgrade" + if(m_currentRequest->getHeader(QtHttpHeader::Upgrade).toLower() == "websocket") { - // Catch websocket header "Upgrade" - if(m_currentRequest->getHeader(QtHttpHeader::Upgrade) == "websocket") + if(m_websocketClient == Q_NULLPTR) { - if(m_websocketClient == Q_NULLPTR) + // 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) { - // 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); + 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 = QStringUtils::split(path,'/', QStringUtils::SplitBehavior::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; } - - // 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); - - // catch /jsonrpc in url, we need async callback, StaticFileServing is sync - QString path = m_currentRequest->getUrl ().path (); - - QStringList uri_parts = QStringUtils::split(path,'/', QStringUtils::SplitBehavior::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 - 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; - } + 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; + } } } } @@ -315,10 +321,9 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt { if (!reply->useChunked ()) { - //reply->appendRawData (CRLF); // send all headers and all data in one shot - reply->requestSendHeaders (); - reply->requestSendData (); + emit reply->requestSendHeaders (); + emit reply->requestSendData (); } else { @@ -331,7 +336,7 @@ QtHttpClientWrapper::ParsingStatus QtHttpClientWrapper::sendReplyToClient (QtHtt { static const QByteArray & CLOSE = QByteArrayLiteral ("close"); - if (m_currentRequest->getHeader(QtHttpHeader::Connection) == CLOSE) + if (m_currentRequest->getHeader(QtHttpHeader::Connection).toLower() == CLOSE) { // must close connection after this request m_sockClient->close (); diff --git a/libsrc/webserver/QtHttpClientWrapper.h b/libsrc/webserver/QtHttpClientWrapper.h index 5f3f31c6..fd2d9f64 100644 --- a/libsrc/webserver/QtHttpClientWrapper.h +++ b/libsrc/webserver/QtHttpClientWrapper.h @@ -58,6 +58,7 @@ private: const bool m_localConnection; WebSocketClient * m_websocketClient; WebJsonRpc * m_webJsonRpc; + QByteArray m_fragment; }; #endif // QTHTTPCLIENTWRAPPER_H