add server proto

This commit is contained in:
Evgenii Alekseev 2017-03-29 03:46:42 +03:00
parent 505c1e2b93
commit 511de6e2d0
23 changed files with 595 additions and 21 deletions

View File

@ -75,6 +75,7 @@ get_directory_property(CMAKE_DEFINITIONS COMPILE_DEFINITIONS)
configure_file("${CMAKE_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
add_subdirectory("queued")
add_subdirectory("queued-daemon")
add_subdirectory("queued-server")
add_subdirectory("queuedctl")
if (BUILD_TESTING)
enable_testing()

View File

@ -1,12 +1,15 @@
# main qt libraries
find_package(Qt5 5.8.0 REQUIRED COMPONENTS Core DBus Sql Test)
find_package(Qt5 5.8.0 REQUIRED COMPONENTS Core DBus Network Sql Test)
add_definitions(
${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} ${Qt5Sql_DEFINITIONS}
${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} ${Qt5Network_DEFINITIONS}
${Qt5Sql_DEFINITIONS}
)
set(Qt_INCLUDE
${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${Qt5Network_INCLUDE_PATH}
${Qt5Sql_INCLUDE_DIRS}
)
set(Qt_LIBRARIES
${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${Qt5Sql_LIBRARIES}
${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${Qt5Network_LIBRARIES}
${Qt5Sql_LIBRARIES}
)

View File

@ -2,7 +2,6 @@
set (SUBPROJECT "queued-daemon")
message (STATUS "Subproject ${SUBPROJECT}")
include("adds.cmake")
add_subdirectory ("src")
# build man
file (GLOB SUBPROJECT_MAN_IN "*.1")

View File

@ -0,0 +1,13 @@
# set project name
set (SUBPROJECT "queued-server")
message (STATUS "Subproject ${SUBPROJECT}")
add_subdirectory ("src")
# build man
file (GLOB SUBPROJECT_MAN_IN "*.1")
file (RELATIVE_PATH SUBPROJECT_MAN "${CMAKE_SOURCE_DIR}" "${SUBPROJECT_MAN_IN}")
configure_file ("${SUBPROJECT_MAN_IN}" "${CMAKE_CURRENT_BINARY_DIR}/${SUBPROJECT_MAN}")
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${SUBPROJECT_MAN}" DESTINATION "${DATA_INSTALL_DIR}/man/man1")
install (FILES "bash-completions" DESTINATION "${DATA_INSTALL_DIR}/bash-completion/completions" RENAME "${SUBPROJECT}")
install (FILES "zsh-completions" DESTINATION "${DATA_INSTALL_DIR}/zsh/site-functions" RENAME "_${SUBPROJECT}")

View File

View File

View File

@ -0,0 +1,18 @@
# set files
file (GLOB_RECURSE SUBPROJECT_SOURCES "*.cpp")
file (GLOB_RECURSE SUBPROJECT_HEADERS "*.h")
# include_path
include_directories ("${PROJECT_LIBRARY_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_BINARY_DIR}"
"${PROJECT_TRDPARTY_DIR}"
"${Qt_INCLUDE}")
qt5_wrap_cpp (SUBPROJECT_MOC_SOURCES "${SUBPROJECT_HEADERS}")
add_executable ("${SUBPROJECT}" "${SUBPROJECT_HEADERS}" "${SUBPROJECT_SOURCES}"
"${SUBPROJECT_MOC_SOURCES}")
target_link_libraries ("${SUBPROJECT}" "${PROJECT_LIBRARY}" "${Qt_LIBRARIES}")
# install properties
install (TARGETS "${SUBPROJECT}" DESTINATION "${BIN_INSTALL_DIR}")

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include "QueuedServer.h"
#include <queued/Queued.h>
#include "QueuedTcpServer.h"
QueuedServer::QueuedServer(QObject *parent, const QVariantHash &args)
: QObject(parent)
, m_configuration(args)
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
init();
}
QueuedServer::~QueuedServer()
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
deinit();
}
void QueuedServer::deinit()
{
if (m_server)
delete m_server;
}
void QueuedServer::init()
{
deinit();
m_server = new QueuedTcpServer(this);
m_server->listen(
QHostAddress(QueuedCoreAdaptor::getOption("ServerAddress").toString()),
QueuedCoreAdaptor::getOption("ServerPort").toUInt());
m_server->setMaxPendingConnections(
QueuedCoreAdaptor::getOption("ServerMaxConnections").toInt());
qCInfo(LOG_SERV) << "Server listen on" << m_server->serverAddress()
<< m_server->serverPort();
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#ifndef QUEUEDSERVER_H
#define QUEUEDSERVER_H
#include <QObject>
#include <QVariant>
class QueuedTcpServer;
class QueuedServer : public QObject
{
Q_OBJECT
public:
explicit QueuedServer(QObject *parent, const QVariantHash &args);
virtual ~QueuedServer();
void deinit();
void init();
private:
// backend
QueuedTcpServer *m_server = nullptr;
// configuration
QVariantHash m_configuration;
};
#endif /* QUEUEDSERVER_H */

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include "QueuedTcpServer.h"
#include <queued/Queued.h>
#include "QueuedTcpServerThread.h"
QueuedTcpServer::QueuedTcpServer(QObject *parent)
: QTcpServer(parent)
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
}
QueuedTcpServer::~QueuedTcpServer()
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
deinit();
}
void QueuedTcpServer::deinit()
{
}
void QueuedTcpServer::init()
{
deinit();
}
void QueuedTcpServer::incomingConnection(qintptr socketDescriptor)
{
QueuedTcpServerThread *thread
= new QueuedTcpServerThread(socketDescriptor, this);
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#ifndef QUEUEDTCPSERVER_H
#define QUEUEDTCPSERVER_H
#include <QTcpServer>
class QueuedTcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit QueuedTcpServer(QObject *parent);
virtual ~QueuedTcpServer();
void deinit();
void init();
protected:
void incomingConnection(qintptr socketDescriptor) override;
private:
};
#endif /* QUEUEDTCPSERVER_H */

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include "QueuedTcpServerThread.h"
#include <QDataStream>
#include <QJsonDocument>
#include <QUrl>
#include <QUrlQuery>
#include <queued/Queued.h>
QueuedTcpServerThread::QueuedTcpServerThread(int socketDescriptor,
QObject *parent)
: QThread(parent)
, m_socketDescriptor(socketDescriptor)
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
}
QueuedTcpServerThread::~QueuedTcpServerThread()
{
qCDebug(LOG_SERV) << __PRETTY_FUNCTION__;
}
QList<QByteArray> QueuedTcpServerThread::defaultResponse(const int code)
{
qCDebug(LOG_SERV) << "Build server response with code" << code;
QList<QByteArray> output;
output += "HTTP/1.1 " + QByteArray::number(code) + " OK\r\n";
output += "Server: QueuedServer/Qt" + QByteArray(qVersion()) + "\r\n";
output += "Date: "
+ QLocale::c()
.toString(QDateTime::currentDateTimeUtc(),
"ddd, d MMM yyyy HH:mm:dd t")
.toUtf8()
+ "\r\n";
output += "Content-Type: application/json\r\n";
output += "\r\n";
return output;
}
QueuedTcpServerThread::QueuedTcpServerRequest
QueuedTcpServerThread::getRequest(const QStringList &headers,
const QByteArray &body)
{
qCDebug(LOG_SERV) << "Get request object from headers" << headers
<< "and body" << body;
QueuedTcpServerThread::QueuedTcpServerRequest request;
request.valid = true;
// method
request.request = headers.first().split(' ').at(0);
// path
QUrl url(headers.first().split(' ').at(1));
request.path = url.path();
// body
QJsonParseError error;
auto jsonDoc = QJsonDocument::fromJson(body, &error);
if (error.error != QJsonParseError::NoError) {
qCWarning(LOG_SERV) << "Parse error" << error.errorString();
request.valid = false;
} else {
request.data = jsonDoc.object().toVariantHash();
}
// append from url if any
auto items = QUrlQuery(url.query()).queryItems();
for (auto &item : items) {
auto key = item.first;
auto value = item.second;
QVariantList values;
switch (request.data[key].type()) {
case QVariant::List:
values = request.data[key].toList();
break;
case QVariant::Invalid:
break;
default:
values = QVariantList({request.data[key]});
break;
}
values.append(key);
request.data[key] = values.count() == 1 ? values.first() : values;
}
return request;
}
void QueuedTcpServerThread::run()
{
m_socket = new QTcpSocket(this);
if (!m_socket->setSocketDescriptor(m_socketDescriptor)) {
emit(error(m_socket->error()));
return;
}
connect(m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()),
Qt::DirectConnection);
connect(m_socket, SIGNAL(disconnected()), this, SLOT(disconnected()),
Qt::DirectConnection);
exec();
}
void QueuedTcpServerThread::disconnected()
{
m_socket->deleteLater();
exit(0);
}
void QueuedTcpServerThread::readyRead()
{
QStringList headers;
while (m_socket->canReadLine())
headers += m_socket->readLine().simplified();
// request body
auto body = m_socket->readAll().simplified();
// get request object
auto request = getRequest(headers, body);
if (!request.valid) {
emit(error(QTcpSocket::UnsupportedSocketOperationError));
return;
}
auto response = defaultResponse(200);
for (auto &resp : response) {
m_socket->write(resp);
m_socket->flush();
}
m_socket->waitForBytesWritten(3000);
m_socket->disconnectFromHost();
if (m_socket->state() != QAbstractSocket::UnconnectedState)
m_socket->waitForDisconnected();
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#ifndef QUEUEDTCPSERVERTHREAD_H
#define QUEUEDTCPSERVERTHREAD_H
#include <QTcpSocket>
#include <QThread>
class QTcpSocket;
class QueuedTcpServerThread : public QThread
{
Q_OBJECT
public:
typedef struct {
QString request;
QString path;
QVariantHash data;
bool valid = false;
} QueuedTcpServerRequest;
explicit QueuedTcpServerThread(int socketDescriptor, QObject *parent);
virtual ~QueuedTcpServerThread();
static QList<QByteArray> defaultResponse(const int code);
static QueuedTcpServerRequest getRequest(const QStringList &headers,
const QByteArray &body);
QVariantHash response(const QueuedTcpServerRequest &request) const;
void run() override;
signals:
void error(QTcpSocket::SocketError socketError);
private slots:
void disconnected();
void readyRead();
private:
QTcpSocket *m_socket = nullptr;
int m_socketDescriptor;
};
#endif /* QUEUEDTCPSERVERTHREAD_H */

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2017 Evgeniy Alekseev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
*
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*/
#include <QCommandLineParser>
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <queued/Queued.h>
#include <iostream>
#include "QueuedServer.h"
#include "version.h"
extern "C" {
#include <signal.h>
#include <unistd.h>
}
QueuedServer *instance = nullptr;
int main(int argc, char *argv[])
{
// HACK preparse arguments to find out if --daemon is set
for (int i = 0; i < argc; i++) {
if (std::string(argv[i]) != "--daemon")
continue;
::daemon(0, 0);
break;
}
QueuedDebug::applyLogFormat();
QCoreApplication app(argc, argv);
app.setApplicationName(NAME);
app.setApplicationVersion(VERSION);
// parser
QCommandLineParser parser;
parser.setApplicationDescription(
"Daemon for starting jobs to queue of calculations");
parser.addHelpOption();
parser.addVersionOption();
// info
QCommandLineOption infoOption(QStringList() << "i"
<< "info",
"Show additional info.");
parser.addOption(infoOption);
// debug mode
QCommandLineOption debugOption(QStringList() << "d"
<< "debug",
"Print debug information.");
parser.addOption(debugOption);
// daemon mode
QCommandLineOption daemonOption(QStringList() << "daemon",
"Start detached.");
parser.addOption(daemonOption);
parser.process(app);
// show info and exit
if (parser.isSet(infoOption)) {
auto metadata = QueuedDebug::getBuildData();
for (auto &string : metadata)
std::cout << qPrintable(string) << std::endl;
return 0;
}
// enable debug
if (parser.isSet(debugOption))
QueuedDebug::enableDebug();
QVariantHash arguments;
// start application
instance = new QueuedServer(nullptr, arguments);
// catch SIGHUP
signal(SIGHUP, [](int sig) -> void {
qCInfo(LOG_SERV) << "Received SIGHUP signal, reinit components";
instance->init();
});
return app.exec();
}

View File

View File

@ -3,6 +3,7 @@ message (STATUS "Subproject ${SUBPROJECT}")
configure_file ("QueuedConfig.h.in" "${CMAKE_BINARY_DIR}/QueuedConfig.h")
include("adds.cmake")
add_subdirectory ("src")
# headers
install (DIRECTORY "include/${SUBPROJECT}" DESTINATION "${INCLUDE_INSTALL_DIR}")

View File

@ -24,24 +24,27 @@
# @brief Queued common libraries
##
find_package(Qt5 5.8.0 REQUIRED COMPONENTS Core DBus Sql)
find_package(Qt5 5.8.0 REQUIRED COMPONENTS Core DBus Network Sql)
##
# @brief add Qt definitions
##
add_definitions(
${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} ${Qt5Sql_DEFINITIONS}
${Qt5Core_DEFINITIONS} ${Qt5DBus_DEFINITIONS} ${Qt5Network_DEFINITIONS}
${Qt5Sql_DEFINITIONS}
)
##
# @def Qt_INCLUDE
# Qt include paths
##
set(Qt_INCLUDE
${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${Qt5Sql_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}
${Qt5Sql_INCLUDE_DIRS}
)
##
# @def Qt_LIBRARIES
# Qt libraries
##
set(Qt_LIBRARIES
${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${Qt5Sql_LIBRARIES}
${Qt5Core_LIBRARIES} ${Qt5DBus_LIBRARIES} ${Qt5Network_LIBRARIES}
${Qt5Sql_LIBRARIES}
)

View File

@ -96,18 +96,27 @@ typedef struct {
* control process command line
* @var QueuedSettings::Plugins
* plugin list
* @var QueuedSettings::ServerAddress
* queued server bind address
* @var QueuedSettings::ServerPort
* queued server bind port
* @var QueuedSettings::ServerMaxConnections
* queued server max connections
*/
enum class QueuedSettings {
Invalid = 1 << 0,
DatabaseInterval = 1 << 1,
DefaultLimits = 1 << 2,
KeepTasks = 1 << 3,
KeepUsers = 1 << 4,
OnExitAction = 1 << 5,
TokenExpiration = 1 << 6,
DatabaseVersion = 1 << 7,
ProcessCommandLine = 1 << 8,
Plugins = 1 << 9,
Invalid,
DatabaseInterval,
DefaultLimits,
KeepTasks,
KeepUsers,
OnExitAction,
TokenExpiration,
DatabaseVersion,
ProcessCommandLine,
Plugins,
ServerAddress,
ServerPort,
ServerMaxConnections
};
/**
* @struct QueuedSettingsField
@ -143,7 +152,10 @@ const QueuedSettingsDefaultMap QueuedSettingsDefaults
{QueuedSettings::ProcessCommandLine,
"systemd-run\n--scope\n--unit={name}\n--uid={uid}\n--gid={gid}"
"\n-p\nCPUQuota={cpu}%\n-p\nMemoryHigh={memory}\n{application}"}},
{"Plugins", {QueuedSettings::Plugins, ""}}};
{"Plugins", {QueuedSettings::Plugins, ""}},
{"ServerAddress", {QueuedSettings::ServerAddress, ""}},
{"ServerPort", {QueuedSettings::ServerPort, 8080}},
{"ServerMaxConnections", {QueuedSettings::ServerMaxConnections, 30}}};
};
#endif /* QUEUEDCONFIGURATION_H */

View File

@ -606,8 +606,8 @@ void QueuedCore::init(const QString &_configuration)
// init parts
initSettings(_configuration);
initPlugins();
initUsers();
initPlugins();
initProcesses();
// settings update notifier
@ -666,6 +666,11 @@ void QueuedCore::updateSettings(const QueuedConfig::QueuedSettings _id,
case QueuedConfig::QueuedSettings::ProcessCommandLine:
m_processes->setProcessLine(_value.toString());
break;
case QueuedConfig::QueuedSettings::ServerAddress:
case QueuedConfig::QueuedSettings::ServerPort:
case QueuedConfig::QueuedSettings::ServerMaxConnections:
// do nothing here
break;
case QueuedConfig::QueuedSettings::TokenExpiration:
m_users->setTokenExpiration(_value.toLongLong());
break;