#pragma once #include "CppCommon/Common/Basic/JDefines.h" #include "CppCommon/Common/Basic/JTerminal.h" #include "CppCommon/Common/Basic/JFile.h" #include "CppCommon/Common/Basic/JConfig.h" #include "exeGetEvent.h" #include "ParsingQuery.h" #include "MakeResponse.h" #include "ExeGet.h" #include "ExePost.h" #include #include #include #include namespace Incart::Net::WebApi { using QMapWebApiGet = QMap; using QMapWebApiPost = QMap; struct SocketInfo { QTcpSocket* socket; size_t contentLength; }; class WebApiServer : public QTcpServer { Q_OBJECT const bool MYDEBUG = false; protected: const QMapWebApiGet& m_exeGet; const QMapWebApiPost& m_exePost; ParsingQuery m_ParsingQuery; MakeResponse* m_Response; // создаем только 1 раз exeGetEvent* m_exeGetEvent = nullptr; MakeResponse m_optionsResponse; std::vector m_processedSockets; public: WebApiServer(quint16 port, const QMapWebApiGet& exeGet, const QMapWebApiPost& exePost, QObject* parent = nullptr) : QTcpServer(parent) , m_exeGet(exeGet) , m_exePost(exePost) { Common::JT() << __PRETTY_FUNCTION__ << port; if (exeGet.contains("event")) { m_exeGetEvent = (exeGetEvent*)exeGet["event"]; } QHostAddress host = QHostAddress::Any; //QHostAddress host("192.168.4.157"); if (listen(host,port)) { Common::JTerminal(JCL_GREEN) << __PRETTY_FUNCTION__ << "listen...." << serverAddress().toString() << serverPort() << maxPendingConnections(); } else { Common::JTerminal(JCL_YELLOW) << __PRETTY_FUNCTION__ << "Error" << errorString(); } connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)),this, SLOT(onAcceptError(QAbstractSocket::SocketError)) ); connect(this, SIGNAL(newConnection()), this, SLOT(onNewConnection()) ); //connect(m_SslTimer,&QTimer::timeout, this, &WebTcpIpServer::slotTimerInterrupt ); //m_SslTimer->start(2000); // for(QString key : m_exeGet.keys()) m_exeGet[key]->set(&m_Response); // for(QString key : m_exePost.keys()) m_exePost[key]->set(&m_Response); } ~WebApiServer() override { Common::JT() << __PRETTY_FUNCTION__; /* for( QTcpSocket* obj : m_SSE_Sockets) { obj->close(); obj->deleteLater(); } */ if (m_exeGetEvent != nullptr) { m_exeGetEvent->ClearEvents(); } } public slots: void onAcceptError(QAbstractSocket::SocketError socketError) { Common::JT() << __PRETTY_FUNCTION__ << socketError; } void onNewConnection() { Common::JT() << "-------------------------------------------------------------------------------"; QTcpSocket* socket = nextPendingConnection(); Common::JT() << __PRETTY_FUNCTION__ << socket << socket->peerAddress() << socket->peerPort() << socket->peerName(); connect(socket, SIGNAL(readyRead()),this, SLOT(onReadyRead()) ); connect(socket, SIGNAL(disconnected()),this, SLOT(onDisConnected()) ); } void onDisConnected() { QTcpSocket* socket = qobject_cast(sender()); Common::JT() << __PRETTY_FUNCTION__ << socket; disconnect(socket, SIGNAL(readyRead()),this, SLOT(onReadyRead()) ); disconnect(socket, SIGNAL(disconnected()),this, SLOT(onDisConnected()) ); socket->close(); socket->deleteLater(); if (m_exeGetEvent != nullptr) { m_exeGetEvent->CloseOneEvent(socket); } //if ( m_SSE_Sockets.contains(socket) ) m_SSE_Sockets.removeOne(socket); } void onReadyRead() { QTcpSocket* socket = qobject_cast(sender()); QByteArray receivedBytes = socket->readAll(); /* std::cout << "====== onReadyRead ===========" << std::endl; QString receivedBytesText(receivedBytes); std::cout << receivedBytesText.toStdString() << std::endl; std::cout << "==============================" << std::endl; */ if (isProcessedSocket(socket)) { QByteArray& body = m_ParsingQuery.getHttp().getBody(); body += receivedBytes; SocketInfo socketInfo = getProcessedSocket(socket); if (body.size() >= static_cast(socketInfo.contentLength)) { freeSocket(socket); executor(m_ParsingQuery, socket); } } else { m_ParsingQuery(receivedBytes); // разбор кадра //Common::JT() << __PRETTY_FUNCTION__ << m_ParsingQuery.getHttp(); // вывод кадра запроса executor(m_ParsingQuery, socket); // исполнитель команд } } protected: bool isProcessedSocket(QTcpSocket* socket_) { for (size_t i = 0; i < m_processedSockets.size(); i++) { if (m_processedSockets[i].socket == socket_) { return true; } } return false; } SocketInfo getProcessedSocket(QTcpSocket* socket_) { for (size_t i = 0; i < m_processedSockets.size(); i++) { if (m_processedSockets[i].socket == socket_) { return m_processedSockets[i]; } } return SocketInfo(); } void freeSocket(QTcpSocket* socket_) { for (auto it = m_processedSockets.begin(); it != m_processedSockets.end(); ++it) { if (it->socket == socket_) { m_processedSockets.erase(it); return; } } } void executor(ParsingQuery& query, QTcpSocket* socket) { // исполнитель команд QString& method = query.getMethod(); ParsingURI& uri = query.getURI(); QString command = uri.getPath(); //!!.getCommand(); QByteArray& body = query.getHttp().getBody(); auto& headers = query.getHttp().getHeaders(); int contentLengthIndex = -1; for (int i = 0; i < headers.size(); ++i) { if (headers[i].name == "Content-Length") { contentLengthIndex = i; break; } } // иногда тело запроса может прийти отдельно от заголовка if ((body.size() == 0) && (contentLengthIndex >= 0)) { int contentLength = headers[contentLengthIndex].value.toInt(); m_processedSockets.push_back(SocketInfo{socket, static_cast(contentLength)}); return; } Common::JT() << __PRETTY_FUNCTION__ << command; //std::cout << __PRETTY_FUNCTION__ << " command=" << command.toUtf8().data() << std::endl; if (method == "GET") { if (!m_exeGet.contains(command)) { command = "__file"; } m_exeGet[command]->exe(uri, socket); m_Response = m_exeGet[command]->getMakeResponse(); } else if (method == "POST") { if (!m_exePost.contains(command)) { command = "__err"; } m_exePost[command]->exe(uri, body, socket); m_Response = m_exePost[command]->getMakeResponse(); } // preflight from browser before normal request else if (method == "OPTIONS") { m_optionsResponse.ret200Options(); socket->write(m_optionsResponse.getResponse()); socket->disconnectFromHost(); m_Response = &m_optionsResponse; } else { Common::JTerminal(JCL_YELLOW) << __PRETTY_FUNCTION__ << "error"; m_exeGet["__err"]->exe(uri, socket); m_Response = m_exeGet["__err"]->getMakeResponse(); } } }; } // namespace Incart::Net::WebApi