mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	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:
		@@ -298,7 +298,7 @@ $(document).ready(function() {
 | 
			
		||||
 | 
			
		||||
	createCP('cp2', cpcolor, function(rgbT,hex){
 | 
			
		||||
		rgb = rgbT;
 | 
			
		||||
		sendColor()
 | 
			
		||||
		sendColor();
 | 
			
		||||
		setStorage('rmcpcolor', hex);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -180,8 +180,6 @@ void LinearColorSmoothing::componentStateChange(const hyperion::Components compo
 | 
			
		||||
 | 
			
		||||
void LinearColorSmoothing::setEnable(bool enable)
 | 
			
		||||
{
 | 
			
		||||
	LedDevice::setEnable(enable);
 | 
			
		||||
 | 
			
		||||
	if (!enable)
 | 
			
		||||
	{
 | 
			
		||||
		_timer->stop();
 | 
			
		||||
 
 | 
			
		||||
@@ -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<QByteArray> parts = data.split('&');
 | 
			
		||||
                        for (int i = 0; i < parts.size(); ++i)
 | 
			
		||||
                        {
 | 
			
		||||
                            QList<QByteArray> 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<QByteArray> parts = data.split('&');
 | 
			
		||||
 | 
			
		||||
						for (int i = 0; i < parts.size(); ++i)
 | 
			
		||||
						{
 | 
			
		||||
							QList<QByteArray> 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 ("<h1>Bad Request (HTTP parsing error) !</h1>"));
 | 
			
		||||
                    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 ("<h1>Bad Request (HTTP parsing error) !</h1>"));
 | 
			
		||||
					reply.appendRawData (CRLF);
 | 
			
		||||
					m_parsingStatus = sendReplyToClient (&reply);
 | 
			
		||||
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
				{
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QtHttpClientWrapper::onReplySendHeadersRequested (void) {
 | 
			
		||||
    QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
 | 
			
		||||
    if (reply != Q_NULLPTR) {
 | 
			
		||||
        QByteArray data;
 | 
			
		||||
        // HTTP Version + Status Code + Status Msg
 | 
			
		||||
        data.append (QtHttpServer::HTTP_VERSION);
 | 
			
		||||
        data.append (SPACE);
 | 
			
		||||
        data.append (QByteArray::number (reply->getStatusCode ()));
 | 
			
		||||
        data.append (SPACE);
 | 
			
		||||
        data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ()));
 | 
			
		||||
        data.append (CRLF);
 | 
			
		||||
        // Header name: header value
 | 
			
		||||
        if (reply->useChunked ()) {
 | 
			
		||||
            static const QByteArray & CHUNKED = QByteArrayLiteral ("chunked");
 | 
			
		||||
            reply->addHeader (QtHttpHeader::TransferEncoding, CHUNKED);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            reply->addHeader (QtHttpHeader::ContentLength, QByteArray::number (reply->getRawDataSize ()));
 | 
			
		||||
        }
 | 
			
		||||
        const QList<QByteArray> & headersList = reply->getHeadersList ();
 | 
			
		||||
        foreach (const QByteArray & header, headersList) {
 | 
			
		||||
            data.append (header);
 | 
			
		||||
            data.append (COLON);
 | 
			
		||||
            data.append (SPACE);
 | 
			
		||||
            data.append (reply->getHeader (header));
 | 
			
		||||
            data.append (CRLF);
 | 
			
		||||
        }
 | 
			
		||||
        // empty line
 | 
			
		||||
        data.append (CRLF);
 | 
			
		||||
        m_sockClient->write (data);
 | 
			
		||||
        m_sockClient->flush ();
 | 
			
		||||
    }
 | 
			
		||||
void QtHttpClientWrapper::onReplySendHeadersRequested (void)
 | 
			
		||||
{
 | 
			
		||||
	QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
 | 
			
		||||
 | 
			
		||||
	if (reply != Q_NULLPTR)
 | 
			
		||||
	{
 | 
			
		||||
		QByteArray data;
 | 
			
		||||
		// HTTP Version + Status Code + Status Msg
 | 
			
		||||
		data.append (QtHttpServer::HTTP_VERSION);
 | 
			
		||||
		data.append (SPACE);
 | 
			
		||||
		data.append (QByteArray::number (reply->getStatusCode ()));
 | 
			
		||||
		data.append (SPACE);
 | 
			
		||||
		data.append (QtHttpReply::getStatusTextForCode (reply->getStatusCode ()));
 | 
			
		||||
		data.append (CRLF);
 | 
			
		||||
 | 
			
		||||
		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<QByteArray> & headersList = reply->getHeadersList ();
 | 
			
		||||
 | 
			
		||||
		foreach (const QByteArray & header, headersList)
 | 
			
		||||
		{
 | 
			
		||||
			data.append (header);
 | 
			
		||||
			data.append (COLON);
 | 
			
		||||
			data.append (SPACE);
 | 
			
		||||
			data.append (reply->getHeader (header));
 | 
			
		||||
			data.append (CRLF);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// empty line
 | 
			
		||||
		data.append (CRLF);
 | 
			
		||||
		m_sockClient->write (data);
 | 
			
		||||
		m_sockClient->flush ();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QtHttpClientWrapper::onReplySendDataRequested (void) {
 | 
			
		||||
    QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
 | 
			
		||||
    if (reply != Q_NULLPTR) {
 | 
			
		||||
        // content raw data
 | 
			
		||||
        QByteArray data = reply->getRawData ();
 | 
			
		||||
        if (reply->useChunked ()) {
 | 
			
		||||
            data.prepend (QByteArray::number (data.size (), 16) % CRLF);
 | 
			
		||||
            data.append (CRLF);
 | 
			
		||||
            reply->resetRawData ();
 | 
			
		||||
        }
 | 
			
		||||
        // write to socket
 | 
			
		||||
        m_sockClient->write (data);
 | 
			
		||||
        m_sockClient->flush ();
 | 
			
		||||
    }
 | 
			
		||||
void QtHttpClientWrapper::onReplySendDataRequested (void)
 | 
			
		||||
{
 | 
			
		||||
	QtHttpReply * reply = qobject_cast<QtHttpReply *> (sender ());
 | 
			
		||||
	if (reply != Q_NULLPTR)
 | 
			
		||||
	{
 | 
			
		||||
		// content raw data
 | 
			
		||||
		QByteArray data = reply->getRawData ();
 | 
			
		||||
 | 
			
		||||
		if (reply->useChunked ())
 | 
			
		||||
		{
 | 
			
		||||
			data.prepend (QByteArray::number (data.size (), 16) % CRLF);
 | 
			
		||||
			data.append (CRLF);
 | 
			
		||||
			reply->resetRawData ();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// write to socket
 | 
			
		||||
		m_sockClient->write (data);
 | 
			
		||||
		m_sockClient->flush ();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,70 +6,35 @@
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
 | 
			
		||||
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<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 ();
 | 
			
		||||
	if (!key.isEmpty ())
 | 
			
		||||
	{
 | 
			
		||||
		m_headersHash.insert (key, value);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<QByteArray> 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<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:
 | 
			
		||||
    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<QByteArray, QByteArray> m_headersHash;
 | 
			
		||||
	bool                          m_useChunked;
 | 
			
		||||
	StatusCode                    m_statusCode;
 | 
			
		||||
	QByteArray                    m_data;
 | 
			
		||||
	QtHttpServer *                m_serverHandle;
 | 
			
		||||
	QHash<QByteArray, QByteArray> m_headersHash;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // QTHTTPREPLY_H
 | 
			
		||||
 
 | 
			
		||||
@@ -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<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;
 | 
			
		||||
	if (!key.isEmpty ())
 | 
			
		||||
	{
 | 
			
		||||
		m_headersHash.insert (key, value);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,45 +14,50 @@ class QtHttpClientWrapper;
 | 
			
		||||
 | 
			
		||||
using QtHttpPostData = QMap<QString,QByteArray>;
 | 
			
		||||
 | 
			
		||||
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<QByteArray>     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<QByteArray>     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<QByteArray, QByteArray> m_headersHash;
 | 
			
		||||
    ClientInfo                    m_clientInfo;
 | 
			
		||||
	QUrl                          m_url;
 | 
			
		||||
	QString                       m_command;
 | 
			
		||||
	QByteArray                    m_data;
 | 
			
		||||
	QtHttpServer *                m_serverHandle;
 | 
			
		||||
	QtHttpClientWrapper *         m_clientHandle;
 | 
			
		||||
	QHash<QByteArray, QByteArray> m_headersHash;
 | 
			
		||||
	ClientInfo                    m_clientInfo;
 | 
			
		||||
	QtHttpPostData                m_postData;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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<QTcpSocket*> 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<QSslCertificate> & 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<QSslSocket *> (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<QSslSocket *> (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<QSslError> & errors) {
 | 
			
		||||
    Q_UNUSED (errors)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<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;
 | 
			
		||||
    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<QSslCertificate> & 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<QSslCertificate> & 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<QSslError> & 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<QSslError> & errors) { Q_UNUSED (errors) };
 | 
			
		||||
	void onClientSslModeChanged     (QSslSocket::SslMode mode)        { Q_UNUSED (mode)   };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool                                       m_useSsl;
 | 
			
		||||
    QSslKey                                    m_sslKey;
 | 
			
		||||
    QList<QSslCertificate>                     m_sslCerts;
 | 
			
		||||
    QString                                    m_serverName;
 | 
			
		||||
	bool                                       m_useSsl;
 | 
			
		||||
	QSslKey                                    m_sslKey;
 | 
			
		||||
	QList<QSslCertificate>                     m_sslCerts;
 | 
			
		||||
	QString                                    m_serverName;
 | 
			
		||||
	NetOrigin*                                 m_netOrigin;
 | 
			
		||||
    QtHttpServerWrapper *                      m_sockServer;
 | 
			
		||||
    QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash;
 | 
			
		||||
	QtHttpServerWrapper *                      m_sockServer;
 | 
			
		||||
	QHash<QTcpSocket *, QtHttpClientWrapper *> m_socksClientsHash;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // QTHTTPSERVER_H
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ public:
 | 
			
		||||
	struct WebSocketHeader
 | 
			
		||||
	{
 | 
			
		||||
		bool          fin;
 | 
			
		||||
		quint8 opCode;
 | 
			
		||||
		quint8        opCode;
 | 
			
		||||
		bool          masked;
 | 
			
		||||
		quint64       payloadLength;
 | 
			
		||||
		char          key[4];
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user