Merge pull request #464 from G10h4ck/libresapilocal

ApiServerLocal run on it's own thread to avoid stopping UI
This commit is contained in:
Cyril Soler 2016-08-08 20:14:05 +02:00 committed by GitHub
commit c0c7ede97b
2 changed files with 121 additions and 48 deletions

View File

@ -1,12 +1,39 @@
/*
* libresapi local socket server
* Copyright (C) 2016 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ApiServerLocal.h"
#include "JsonStream.h"
namespace resource_api{
ApiServerLocal::ApiServerLocal(ApiServer* server) : QThread(),
mApiServer(server), mLocalServer(this)
ApiServerLocal::ApiServerLocal(ApiServer* server, QObject *parent) :
QObject(parent), serverThread(this),
localListener(server) // Must have no parent to be movable to other thread
{
localListener.moveToThread(&serverThread);
serverThread.start();
}
ApiServerLocal::~ApiServerLocal() { serverThread.quit(); }
ApiLocalListener::ApiLocalListener(ApiServer *server, QObject *parent) :
QObject(parent), mApiServer(server), mLocalServer(this)
{
start();
mLocalServer.removeServer(serverName());
#if QT_VERSION >= 0x050000
mLocalServer.setSocketOptions(QLocalServer::UserAccessOption);
@ -15,56 +42,72 @@ ApiServerLocal::ApiServerLocal(ApiServer* server) : QThread(),
mLocalServer.listen(serverName());
}
ApiServerLocal::~ApiServerLocal()
void ApiLocalListener::handleConnection()
{
mLocalServer.close();
quit();
new ApiLocalConnectionHandler(mApiServer,
mLocalServer.nextPendingConnection(), this);
}
void ApiServerLocal::handleConnection()
ApiLocalConnectionHandler::ApiLocalConnectionHandler(
ApiServer* apiServer, QLocalSocket* sock, QObject *parent) :
QObject(parent), mApiServer(apiServer), mLocalSocket(sock),
mState(WAITING_PATH)
{
new ApiLocalConnectionHandler(mApiServer, mLocalServer.nextPendingConnection());
connect(mLocalSocket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
connect(sock, SIGNAL(readyRead()), this, SLOT(handlePendingRequests()));
}
ApiLocalConnectionHandler::ApiLocalConnectionHandler(ApiServer* apiServer, QLocalSocket* sock) :
QThread(), mApiServer(apiServer), mLocalSocket(sock)
ApiLocalConnectionHandler::~ApiLocalConnectionHandler()
{
sock->moveToThread(this);
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
connect(mLocalSocket, SIGNAL(disconnected()), this, SLOT(quit()));
connect(sock, SIGNAL(readyRead()), this, SLOT(handleRequest()));
start();
mLocalSocket->close();
delete mLocalSocket;
}
void ApiLocalConnectionHandler::handleRequest()
void ApiLocalConnectionHandler::handlePendingRequests()
{
char path[1024];
if(mLocalSocket->readLine(path, 1023) > 0)
switch(mState)
{
reqPath = path;
char jsonData[20480];
if(mLocalSocket->read(jsonData, 20479) > 0)
case WAITING_PATH:
{
if(mLocalSocket->canReadLine())
{
readPath:
reqPath = mLocalSocket->readLine().constData();
mState = WAITING_DATA;
/* Because QLocalSocket is SOCK_STREAM some clients implementations
* like the one based on QLocalSocket feel free to send the whole
* request (PATH + DATA) in a single write(), causing readyRead()
* signal being emitted only once, in that case we should continue
* processing without waiting for readyRead() being fired again, so
* we don't break here as there may be more lines to read */
}
}
case WAITING_DATA:
{
if(mLocalSocket->canReadLine())
{
resource_api::JsonStream reqJson;
reqJson.setJsonString(std::string(jsonData));
reqJson.setJsonString(std::string(mLocalSocket->readLine().constData()));
resource_api::Request req(reqJson);
req.setPath(reqPath);
std::string resultString = mApiServer->handleRequest(req);
mLocalSocket->write(resultString.c_str(), resultString.length());
mLocalSocket->write("\n\0");
mState = WAITING_PATH;
/* Because QLocalSocket is SOCK_STREAM some clients implementations
* like the one based on QLocalSocket feel free to coalesce multiple
* upper level write() into a single socket write(), causing
* readyRead() signal being emitted only once, in that case we should
* keep processing without waiting for readyRead() being fired again */
if(mLocalSocket->canReadLine()) goto readPath;
// Now there are no more requests to process we can break
break;
}
else mLocalSocket->write("\"{\"data\":null,\"debug_msg\":\"ApiLocalConnectionHandler::handleRequest() Error: timeout waiting for path.\\n\",\"returncode\":\"fail\"}\"\n\0");
}
else mLocalSocket->write("{\"data\":null,\"debug_msg\":\"ApiLocalConnectionHandler::handleRequest() Error: timeout waiting for JSON data.\\n\",\"returncode\":\"fail\"}\"\n\0");
quit();
}
ApiLocalConnectionHandler::~ApiLocalConnectionHandler()
{
mLocalSocket->flush();
mLocalSocket->close();
mLocalSocket->deleteLater();
}
}
} // namespace resource_api

View File

@ -1,4 +1,21 @@
#pragma once
/*
* libresapi local socket server
* Copyright (C) 2016 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QLocalServer>
#include <QString>
@ -11,22 +28,14 @@
namespace resource_api
{
class ApiServer;
class ApiServerLocal : public QThread
class ApiLocalListener : public QObject
{
Q_OBJECT
public:
ApiServerLocal(ApiServer* server);
~ApiServerLocal();
public slots:
void handleConnection();
private:
ApiServer* mApiServer;
QLocalServer mLocalServer;
ApiLocalListener(ApiServer* server, QObject *parent=0);
~ApiLocalListener() { mLocalServer.close(); }
const static QString& serverName()
{
@ -34,24 +43,45 @@ private:
.append("/libresapi.sock").c_str());
return sockPath;
}
public slots:
void handleConnection();
private:
ApiServer* mApiServer;
QLocalServer mLocalServer;
};
class ApiLocalConnectionHandler : public QThread
class ApiServerLocal : public QObject
{
Q_OBJECT
public:
ApiLocalConnectionHandler(ApiServer* apiServer, QLocalSocket* sock);
ApiServerLocal(ApiServer* server, QObject *parent=0);
~ApiServerLocal();
private:
QThread serverThread;
ApiLocalListener localListener;
};
class ApiLocalConnectionHandler : public QObject
{
Q_OBJECT
public:
ApiLocalConnectionHandler(ApiServer* apiServer, QLocalSocket* sock, QObject *parent = 0);
~ApiLocalConnectionHandler();
enum State {WAITING_PATH, WAITING_DATA};
public slots:
void handleRequest();
void handlePendingRequests();
private:
ApiServer* mApiServer;
QLocalSocket* mLocalSocket;
State mState;
std::string reqPath;
void _die();
};
} // namespace resource_api