some server implementations

This commit is contained in:
Evgenii Alekseev 2017-04-06 03:27:20 +03:00
parent 80689782de
commit e0833f22a5
14 changed files with 653 additions and 9 deletions

View File

@ -0,0 +1,97 @@
/*
* 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 "QueuedTcpServerResponseHelper.h"
#include <QRegularExpression>
#include <queued/Queued.h>
#include "QueuedTcpServerResponseHelperApi1.h"
QVariantHash QueuedTcpServerResponseHelper::getData(const Request &_request,
const QVariantHash &_data,
const QString &_token)
{
qCDebug(LOG_SERV) << "Get data for request"
<< static_cast<int>(_request.path) << _request.apiVersion
<< _request.arg << "with data" << _data;
QVariantHash output;
if (_request.apiVersion == 1)
output = QueuedTcpServerResponseHelperApi1::getData(
_request.path, _request.arg, _request.type, _data, _token);
return output;
}
QueuedTcpServerResponseHelper::Request
QueuedTcpServerResponseHelper::parsePath(const QString &_path)
{
qCDebug(LOG_SERV) << "Parse path" << _path;
// /api/v1/request/arg or /api/v1/request
QRegularExpression regex("^\\/api\\/v(?<version>\\d+)\\/"
"(?<path>[\\d\\w]*)(\\/(?<arg>[\\d\\w]+))?$");
regex.setPatternOptions(QRegularExpression::DotMatchesEverythingOption);
Request request;
request.valid = false;
QRegularExpressionMatchIterator it = regex.globalMatch(_path);
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
request.apiVersion = match.captured("version").toUInt();
request.arg = match.captured("arg");
request.path = pathToEnum(match.captured("path"));
// check if request is valid
request.valid = (request.path != RequestPath::Unknown)
&& (std::find(std::begin(QueuedConfig::WEBAPI_VERSIONS),
std::end(QueuedConfig::WEBAPI_VERSIONS),
request.apiVersion)
!= std::end(QueuedConfig::WEBAPI_VERSIONS));
}
return request;
}
QueuedTcpServerResponseHelper::RequestPath
QueuedTcpServerResponseHelper::pathToEnum(const QString &_path)
{
qCDebug(LOG_SERV) << "Convert path" << _path;
if (_path == "auth")
return RequestPath::Auth;
else if (_path == "option")
return RequestPath::Option;
else if (_path == "permissions")
return RequestPath::Permissions;
else if (_path == "plugins")
return RequestPath::Plugins;
else if (_path == "reports")
return RequestPath::Reports;
else if (_path == "task")
return RequestPath::Task;
else if (_path == "user")
return RequestPath::User;
return RequestPath::Unknown;
}

View File

@ -0,0 +1,90 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPER_H
#define QUEUEDTCPSERVERRESPONSEHELPER_H
#include <QVariant>
namespace QueuedTcpServerResponseHelper
{
enum class RequestPath {
Unknown,
Auth,
Option,
Permissions,
Plugins,
Reports,
Task,
User
};
typedef struct {
int apiVersion;
QString arg;
RequestPath path;
QString type;
bool valid;
} Request;
const QHash<int, QByteArray> HTTPCodeMap
= {{100, "Continue"},
{101, "Switching Protocols"},
{200, "OK"},
{201, "Created"},
{202, "Accepted"},
{203, "Non-Authoritative Information"},
{204, "No Content"},
{205, "Reset Content"},
{206, "Partial Content"},
{300, "Multiple Choices"},
{301, "Moved Permanently"},
{302, "Found"},
{303, "See Other"},
{304, "Not Modified"},
{305, "Use Proxy"},
{307, "Temporary Redirect"},
{400, "Bad Request"},
{401, "Unauthorized"},
{402, "Payment Required"},
{403, "Forbidden"},
{404, "Not Found"},
{405, "Method Not Allowed"},
{406, "Not Acceptable"},
{407, "Proxy Authentication Required"},
{408, "Request Time-out"},
{409, "Conflict"},
{410, "Gone"},
{411, "Length Required"},
{412, "Precondition Failed"},
{413, "Request Entity Too Large"},
{414, "Request-URI Too Large"},
{415, "Unsupported Media Type"},
{416, "Requested range not satisfiable"},
{417, "Expectation Failed"},
{500, "Internal Server Error"},
{501, "Not Implemented"},
{502, "Bad Gateway"},
{503, "Service Unavailable"},
{504, "Gateway Time-out"},
{505, "HTTP Version not supported"}};
QVariantHash getData(const Request &_request, const QVariantHash &_data,
const QString &_token);
Request parsePath(const QString &_path);
RequestPath pathToEnum(const QString &_path);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPER_H */

View File

@ -0,0 +1,83 @@
/*
* 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 "QueuedTcpServerResponseHelperApi1.h"
#include <queued/Queued.h>
#include "QueuedTcpServerResponseHelperAuth.h"
#include "QueuedTcpServerResponseHelperOption.h"
#include "QueuedTcpServerResponseHelperPermissions.h"
#include "QueuedTcpServerResponseHelperPlugins.h"
QVariantHash QueuedTcpServerResponseHelperApi1::getData(
const QueuedTcpServerResponseHelper::RequestPath _request,
const QString &_arg, const QString &_type, const QVariantHash &_data,
const QString &_token)
{
qCDebug(LOG_SERV) << "Get data for" << static_cast<int>(_request)
<< "with arg" << _arg << "with data" << _data;
QVariantHash output;
if ((_request != QueuedTcpServerResponseHelper::RequestPath::Auth)
&& !QueuedTcpServerResponseHelperAuth::tryAuth(_token)) {
output = {{"code", 401}};
return output;
}
switch (_request) {
case QueuedTcpServerResponseHelper::RequestPath::Auth:
if (_type == "POST")
output = QueuedTcpServerResponseHelperAuth::auth(_data);
else
output = {{"code", 405}};
break;
case QueuedTcpServerResponseHelper::RequestPath::Option:
if (_type == "GET")
output = QueuedTcpServerResponseHelperOption::getOption(_arg);
else if (_type == "POST")
output = QueuedTcpServerResponseHelperOption::setOption(_arg, _data,
_token);
else
output = {{"code", 405}};
break;
case QueuedTcpServerResponseHelper::RequestPath::Permissions:
if (_type == "DELETE")
output = QueuedTcpServerResponseHelperPermissions::removePermission(
_arg.toLongLong(), _data, _token);
else if (_type == "POST")
output = QueuedTcpServerResponseHelperPermissions::addPermission(
_arg.toLongLong(), _data, _token);
else
output = {{"code", 405}};
break;
case QueuedTcpServerResponseHelper::RequestPath::Plugins:
if (_type == "DELETE")
output = QueuedTcpServerResponseHelperPlugins::removePlugin(_arg,
_token);
else if (_type == "GET")
output = QueuedTcpServerResponseHelperPlugins::listPlugins();
else if (_type == "POST")
output
= QueuedTcpServerResponseHelperPlugins::addPlugin(_arg, _token);
else
output = {{"code", 405}};
break;
}
return output;
}

View File

@ -0,0 +1,33 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPERAPI1_H
#define QUEUEDTCPSERVERRESPONSEHELPERAPI1_H
#include <QVariant>
#include "QueuedTcpServerResponseHelper.h"
namespace QueuedTcpServerResponseHelperApi1
{
QVariantHash getData(const QueuedTcpServerResponseHelper::RequestPath _request,
const QString &_arg, const QString &_type,
const QVariantHash &_data, const QString &_token);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPERAPI1_H */

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.
*/
#include "QueuedTcpServerResponseHelperAuth.h"
#include <queued/Queued.h>
QVariantHash QueuedTcpServerResponseHelperAuth::auth(const QVariantHash &_data)
{
qCDebug(LOG_SERV) << "Auth with data" << _data;
QVariantHash output;
if (_data.contains("user") && _data.contains("password")) {
output["token"] = QueuedCoreAdaptor::auth(_data["user"].toString(),
_data["password"].toString());
output["code"] = output["token"].toString().isEmpty() ? 401 : 200;
} else {
output = {{"code", 400}, {"message", "No required fields found"}};
}
return output;
}
bool QueuedTcpServerResponseHelperAuth::tryAuth(const QString &_token)
{
qCDebug(LOG_APP) << "Try auth with" << _token;
return QueuedCoreAdaptor::auth(_token);
}

View File

@ -0,0 +1,30 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPERAUTH_H
#define QUEUEDTCPSERVERRESPONSEHELPERAUTH_H
#include <QVariant>
namespace QueuedTcpServerResponseHelperAuth
{
QVariantHash auth(const QVariantHash &_payload);
bool tryAuth(const QString &_token);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPERAUTH_H */

View File

@ -0,0 +1,43 @@
/*
* 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 "QueuedTcpServerResponseHelperOption.h"
#include <queued/Queued.h>
QVariantHash
QueuedTcpServerResponseHelperOption::getOption(const QString &_option)
{
qCDebug(LOG_SERV) << "Get option" << _option;
return {{"code", 200}, {"value", QueuedCoreAdaptor::getOption(_option)}};
}
QVariantHash QueuedTcpServerResponseHelperOption::setOption(
const QString &_option, const QVariantHash &_value, const QString &_token)
{
qCDebug(LOG_SERV) << "Set option" << _option << "to" << _value;
if (!_value.contains("value"))
return {{"code", 400}, {"message", "No required fields found"}};
return {{"code",
QueuedCoreAdaptor::sendOptionEdit(_option, _value["value"], _token)
? 200
: 400}};
}

View File

@ -0,0 +1,31 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPEROPTION_H
#define QUEUEDTCPSERVERRESPONSEHELPEROPTION_H
#include <QVariant>
namespace QueuedTcpServerResponseHelperOption
{
QVariantHash getOption(const QString &_option);
QVariantHash setOption(const QString &_option, const QVariantHash &_value,
const QString &_token);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPEROPTION_H */

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.
*/
#include "QueuedTcpServerResponseHelperPermissions.h"
#include <queued/Queued.h>
QVariantHash QueuedTcpServerResponseHelperPermissions::addPermission(
const long long _id, const QVariantHash &_value, const QString &_token)
{
qCDebug(LOG_SERV) << "Add permission for" << _id << _value;
if (!_value.contains("permission"))
return {{"code", 400}, {"message", "No required fields found"}};
auto permission
= QueuedEnums::stringToPermission(_value["permission"].toString());
if (permission == QueuedEnums::Permission::Invalid)
return {{"code", 400}, {"message", "Invalid permission"}};
return {{"code",
QueuedCoreAdaptor::sendUserPermissionAdd(_id, permission, _token)
? 200
: 400}};
}
QVariantHash QueuedTcpServerResponseHelperPermissions::removePermission(
const long long _id, const QVariantHash &_value, const QString &_token)
{
qCDebug(LOG_SERV) << "Remove permission for" << _id << _value;
if (!_value.contains("permission"))
return {{"code", 400}, {"message", "No required fields found"}};
auto permission
= QueuedEnums::stringToPermission(_value["permission"].toString());
if (permission == QueuedEnums::Permission::Invalid)
return {{"code", 400}, {"message", "Invalid permission"}};
return {{"code", QueuedCoreAdaptor::sendUserPermissionRemove(
_id, permission, _token)
? 200
: 400}};
}

View File

@ -0,0 +1,32 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPERPERMISSIONS_H
#define QUEUEDTCPSERVERRESPONSEHELPERPERMISSIONS_H
#include <QVariant>
namespace QueuedTcpServerResponseHelperPermissions
{
QVariantHash addPermission(const long long _id, const QVariantHash &_value,
const QString &_token);
QVariantHash removePermission(const long long _id, const QVariantHash &_value,
const QString &_token);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPERPERMISSIONS_H */

View File

@ -0,0 +1,50 @@
/*
* 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 "QueuedTcpServerResponseHelperPlugins.h"
#include <queued/Queued.h>
QVariantHash
QueuedTcpServerResponseHelperPlugins::addPlugin(const QString &_name,
const QString &_token)
{
qCDebug(LOG_SERV) << "Add plugin" << _name;
return {
{"code", QueuedCoreAdaptor::sendPluginAdd(_name, _token) ? 200 : 400}};
}
QVariantHash QueuedTcpServerResponseHelperPlugins::listPlugins()
{
return {{"code", 200},
{"plugins",
QueuedCoreAdaptor::getOption(QueuedAdvancedSettings::internalId(
QueuedConfig::QueuedSettings::Plugins))}};
}
QVariantHash
QueuedTcpServerResponseHelperPlugins::removePlugin(const QString &_name,
const QString &_token)
{
qCDebug(LOG_SERV) << "Remove plugin" << _name;
return {{"code",
QueuedCoreAdaptor::sendPluginRemove(_name, _token) ? 200 : 400}};
}

View File

@ -0,0 +1,31 @@
/*
* 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 QUEUEDTCPSERVERRESPONSEHELPLUGINS_H
#define QUEUEDTCPSERVERRESPONSEHELPLUGINS_H
#include <QVariant>
namespace QueuedTcpServerResponseHelperPlugins
{
QVariantHash addPlugin(const QString &_name, const QString &_token);
QVariantHash listPlugins();
QVariantHash removePlugin(const QString &_name, const QString &_token);
};
#endif /* QUEUEDTCPSERVERRESPONSEHELPLUGINS_H */

View File

@ -24,6 +24,8 @@
#include <queued/Queued.h>
#include "QueuedTcpServerResponseHelper.h"
QueuedTcpServerThread::QueuedTcpServerThread(int socketDescriptor,
QObject *parent)
@ -47,7 +49,8 @@ QByteArrayList QueuedTcpServerThread::defaultResponse(const int code,
<< "and json";
QList<QByteArray> output;
output += "HTTP/1.1 " + QByteArray::number(code) + " OK\r\n";
output += "HTTP/1.1 " + QByteArray::number(code) + " "
+ QueuedTcpServerResponseHelper::HTTPCodeMap[code] + "\r\n";
output += "Server: QueuedServer/Qt" + QByteArray(qVersion()) + "\r\n";
output += "Date: "
+ QLocale::c()
@ -89,8 +92,8 @@ QueuedTcpServerThread::getHeaders(const QStringList &headers)
auto parsed = header.split(": ");
if (parsed.count() < 2)
continue;
headersObj.headers
+= {parsed.first().toUtf8(), parsed.mid(1).join(": ").toUtf8()};
headersObj.headers += {parsed.first().toUtf8().toLower(),
parsed.mid(1).join(": ").toUtf8()};
}
return headersObj;
@ -151,17 +154,27 @@ QueuedTcpServerThread::response(const QueuedTcpServerRequest &request) const
netRequest.setRawHeader(headers.first, headers.second);
// prepend code
if (!netRequest.header(QNetworkRequest::KnownHeaders::ContentTypeHeader)
.toString()
.startsWith("application/json"))
if (netRequest.header(QNetworkRequest::KnownHeaders::ContentTypeHeader)
.toString()
!= "application/json")
response.code = 415;
else
response.code = 200;
QString token = netRequest.rawHeader(QueuedConfig::WEBAPI_TOKEN_HEADER);
// json data
if (response.code == 200)
// TODO json response from helpers
response.data = {{"foo", "bar"}};
if (response.code == 200) {
auto req = QueuedTcpServerResponseHelper::parsePath(
request.headers.query.path());
req.type = request.headers.request;
if (req.valid) {
response.data = QueuedTcpServerResponseHelper::getData(
req, request.data, token);
response.code = response.data["code"].toInt();
} else {
response.code = 404;
}
}
return response;
}

View File

@ -94,6 +94,14 @@ const char PLUGIN_PATH[] = "plugins";
* @brief version of internal storage
*/
const int DATABASE_VERSION = 1;
/**
* @brief header name for token
*/
const char WEBAPI_TOKEN_HEADER[] = "x-queued-token";
/**
* @brief supported web server API versions
*/
const int WEBAPI_VERSIONS[] = {1};
// plugin interfaces
/**