Database Pool
This commit is contained in:
parent
59c1ffd027
commit
fe2fbb9ad0
@ -40,6 +40,7 @@ message("Compile options: " ${COMPILE_OPTIONS_STRING})
|
|||||||
find_package(nlohmann_json REQUIRED)
|
find_package(nlohmann_json REQUIRED)
|
||||||
find_package(FCGI REQUIRED)
|
find_package(FCGI REQUIRED)
|
||||||
find_package(Argon2 REQUIRED)
|
find_package(Argon2 REQUIRED)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} main.cpp)
|
add_executable(${PROJECT_NAME} main.cpp)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
@ -63,6 +64,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
|
|||||||
FCGI::FCGI++
|
FCGI::FCGI++
|
||||||
nlohmann_json::nlohmann_json
|
nlohmann_json::nlohmann_json
|
||||||
Argon2::Argon2
|
Argon2::Argon2
|
||||||
|
Threads::Threads
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
set(HEADERS
|
set(HEADERS
|
||||||
dbinterface.h
|
interface.h
|
||||||
exceptions.h
|
exceptions.h
|
||||||
|
pool.h
|
||||||
|
resource.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
dbinterface.cpp
|
interface.cpp
|
||||||
exceptions.cpp
|
exceptions.cpp
|
||||||
|
pool.cpp
|
||||||
|
resource.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
|
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
|
||||||
DBInterface::Duplicate::Duplicate(const std::string& text):
|
DB::Duplicate::Duplicate(const std::string& text):
|
||||||
std::runtime_error(text)
|
std::runtime_error(text)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
DBInterface::DuplicateLogin::DuplicateLogin(const std::string& text):
|
DB::DuplicateLogin::DuplicateLogin(const std::string& text):
|
||||||
Duplicate(text)
|
Duplicate(text)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
DBInterface::EmptyResult::EmptyResult(const std::string& text):
|
DB::EmptyResult::EmptyResult(const std::string& text):
|
||||||
std::runtime_error(text)
|
std::runtime_error(text)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
DBInterface::NoLogin::NoLogin(const std::string& text):
|
DB::NoLogin::NoLogin(const std::string& text):
|
||||||
EmptyResult(text)
|
EmptyResult(text)
|
||||||
{}
|
{}
|
||||||
|
@ -3,24 +3,27 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "dbinterface.h"
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
class DBInterface::Duplicate : public std::runtime_error {
|
namespace DB {
|
||||||
|
class Duplicate : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit Duplicate(const std::string& text);
|
explicit Duplicate(const std::string& text);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBInterface::DuplicateLogin : public DBInterface::Duplicate {
|
class DuplicateLogin : public Duplicate {
|
||||||
public:
|
public:
|
||||||
explicit DuplicateLogin(const std::string& text);
|
explicit DuplicateLogin(const std::string& text);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBInterface::EmptyResult : public std::runtime_error {
|
class EmptyResult : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
explicit EmptyResult(const std::string& text);
|
explicit EmptyResult(const std::string& text);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DBInterface::NoLogin : public DBInterface::EmptyResult {
|
class NoLogin : public EmptyResult {
|
||||||
public:
|
public:
|
||||||
explicit NoLogin(const std::string& text);
|
explicit NoLogin(const std::string& text);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "dbinterface.h"
|
#include "interface.h"
|
||||||
|
|
||||||
#include "mysql/mysql.h"
|
#include "mysql/mysql.h"
|
||||||
|
|
||||||
DBInterface::DBInterface(Type type):
|
DB::Interface::Interface(Type type):
|
||||||
type(type),
|
type(type),
|
||||||
state(State::disconnected)
|
state(State::disconnected)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
DBInterface::~DBInterface() {}
|
DB::Interface::~Interface() {}
|
||||||
|
|
||||||
std::unique_ptr<DBInterface> DBInterface::create(Type type) {
|
std::unique_ptr<DB::Interface> DB::Interface::create(Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::mysql:
|
case Type::mysql:
|
||||||
return std::make_unique<MySQL>();
|
return std::make_unique<MySQL>();
|
||||||
@ -21,6 +21,6 @@ std::unique_ptr<DBInterface> DBInterface::create(Type type) {
|
|||||||
throw std::runtime_error("Unexpected database type: " + std::to_string((uint8_t)type));
|
throw std::runtime_error("Unexpected database type: " + std::to_string((uint8_t)type));
|
||||||
}
|
}
|
||||||
|
|
||||||
DBInterface::State DBInterface::currentState() const {
|
DB::Interface::State DB::Interface::currentState() const {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
@ -8,7 +8,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
class DBInterface {
|
namespace DB {
|
||||||
|
class Interface {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
mysql
|
mysql
|
||||||
@ -18,19 +19,14 @@ public:
|
|||||||
connecting,
|
connecting,
|
||||||
connected
|
connected
|
||||||
};
|
};
|
||||||
static std::unique_ptr<DBInterface> create(Type type);
|
static std::unique_ptr<Interface> create(Type type);
|
||||||
|
|
||||||
virtual ~DBInterface();
|
virtual ~Interface();
|
||||||
|
|
||||||
State currentState() const;
|
State currentState() const;
|
||||||
|
|
||||||
const Type type;
|
const Type type;
|
||||||
|
|
||||||
class Duplicate;
|
|
||||||
class DuplicateLogin;
|
|
||||||
class EmptyResult;
|
|
||||||
class NoLogin;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void connect(const std::string& path) = 0;
|
virtual void connect(const std::string& path) = 0;
|
||||||
virtual void disconnect() = 0;
|
virtual void disconnect() = 0;
|
||||||
@ -46,8 +42,9 @@ public:
|
|||||||
virtual unsigned int createSession(const std::string& login, const std::string& access, const std::string& renew) = 0;
|
virtual unsigned int createSession(const std::string& login, const std::string& access, const std::string& renew) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DBInterface(Type type);
|
Interface(Type type);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
State state;
|
State state;
|
||||||
};
|
};
|
||||||
|
}
|
@ -23,8 +23,8 @@ constexpr const char* createSessionQuery = "INSERT INTO sessions (`owner`, `acce
|
|||||||
|
|
||||||
static const std::filesystem::path buildSQLPath = "database";
|
static const std::filesystem::path buildSQLPath = "database";
|
||||||
|
|
||||||
MySQL::MySQL():
|
DB::MySQL::MySQL():
|
||||||
DBInterface(Type::mysql),
|
Interface(Type::mysql),
|
||||||
connection(),
|
connection(),
|
||||||
login(),
|
login(),
|
||||||
password(),
|
password(),
|
||||||
@ -33,11 +33,11 @@ MySQL::MySQL():
|
|||||||
mysql_init(&connection);
|
mysql_init(&connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
MySQL::~MySQL() {
|
DB::MySQL::~MySQL() {
|
||||||
mysql_close(&connection);
|
mysql_close(&connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::connect(const std::string& path) {
|
void DB::MySQL::connect(const std::string& path) {
|
||||||
if (state != State::disconnected)
|
if (state != State::disconnected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ void MySQL::connect(const std::string& path) {
|
|||||||
state = State::connected;
|
state = State::connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::setCredentials(const std::string& login, const std::string& password) {
|
void DB::MySQL::setCredentials(const std::string& login, const std::string& password) {
|
||||||
if (MySQL::login == login && MySQL::password == password)
|
if (MySQL::login == login && MySQL::password == password)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ void MySQL::setCredentials(const std::string& login, const std::string& password
|
|||||||
throw std::runtime_error(std::string("Error changing credetials: ") + mysql_error(con));
|
throw std::runtime_error(std::string("Error changing credetials: ") + mysql_error(con));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::setDatabase(const std::string& database) {
|
void DB::MySQL::setDatabase(const std::string& database) {
|
||||||
if (MySQL::database == database)
|
if (MySQL::database == database)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ void MySQL::setDatabase(const std::string& database) {
|
|||||||
throw std::runtime_error(std::string("Error changing db: ") + mysql_error(con));
|
throw std::runtime_error(std::string("Error changing db: ") + mysql_error(con));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::disconnect() {
|
void DB::MySQL::disconnect() {
|
||||||
if (state == State::disconnected)
|
if (state == State::disconnected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ void MySQL::disconnect() {
|
|||||||
mysql_init(con); //this is ridiculous!
|
mysql_init(con); //this is ridiculous!
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::executeFile(const std::filesystem::path& relativePath) {
|
void DB::MySQL::executeFile(const std::filesystem::path& relativePath) {
|
||||||
MYSQL* con = &connection;
|
MYSQL* con = &connection;
|
||||||
std::filesystem::path path = sharedPath() / relativePath;
|
std::filesystem::path path = sharedPath() / relativePath;
|
||||||
if (!std::filesystem::exists(path))
|
if (!std::filesystem::exists(path))
|
||||||
@ -138,7 +138,7 @@ void MySQL::executeFile(const std::filesystem::path& relativePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MySQL::getVersion() {
|
uint8_t DB::MySQL::getVersion() {
|
||||||
MYSQL* con = &connection;
|
MYSQL* con = &connection;
|
||||||
int result = mysql_query(con, versionQuery);
|
int result = mysql_query(con, versionQuery);
|
||||||
|
|
||||||
@ -161,14 +161,14 @@ uint8_t MySQL::getVersion() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::setVersion(uint8_t version) {
|
void DB::MySQL::setVersion(uint8_t version) {
|
||||||
std::string strVersion = std::to_string(version);
|
std::string strVersion = std::to_string(version);
|
||||||
Statement statement(&connection, updateQuery);
|
Statement statement(&connection, updateQuery);
|
||||||
statement.bind(strVersion.data(), MYSQL_TYPE_VAR_STRING);
|
statement.bind(strVersion.data(), MYSQL_TYPE_VAR_STRING);
|
||||||
statement.execute();
|
statement.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::migrate(uint8_t targetVersion) {
|
void DB::MySQL::migrate(uint8_t targetVersion) {
|
||||||
uint8_t currentVersion = getVersion();
|
uint8_t currentVersion = getVersion();
|
||||||
|
|
||||||
while (currentVersion < targetVersion) {
|
while (currentVersion < targetVersion) {
|
||||||
@ -190,7 +190,7 @@ void MySQL::migrate(uint8_t targetVersion) {
|
|||||||
std::cout << "Database is now on actual version " << std::to_string(targetVersion) << std::endl;
|
std::cout << "Database is now on actual version " << std::to_string(targetVersion) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> MySQL::getComment(std::string& string) {
|
std::optional<std::string> DB::MySQL::getComment(std::string& string) {
|
||||||
ltrim(string);
|
ltrim(string);
|
||||||
if (string.length() < 2)
|
if (string.length() < 2)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -218,7 +218,7 @@ std::optional<std::string> MySQL::getComment(std::string& string) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MySQL::registerAccount(const std::string& login, const std::string& hash) {
|
unsigned int DB::MySQL::registerAccount(const std::string& login, const std::string& hash) {
|
||||||
//TODO validate filed lengths!
|
//TODO validate filed lengths!
|
||||||
MYSQL* con = &connection;
|
MYSQL* con = &connection;
|
||||||
MySQL::Transaction txn(con);
|
MySQL::Transaction txn(con);
|
||||||
@ -247,7 +247,7 @@ unsigned int MySQL::registerAccount(const std::string& login, const std::string&
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MySQL::getAccountHash(const std::string& login) {
|
std::string DB::MySQL::getAccountHash(const std::string& login) {
|
||||||
std::string l = login;
|
std::string l = login;
|
||||||
MYSQL* con = &connection;
|
MYSQL* con = &connection;
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ std::string MySQL::getAccountHash(const std::string& login) {
|
|||||||
return std::any_cast<const std::string&>(result[0][0]);
|
return std::any_cast<const std::string&>(result[0][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MySQL::createSession(const std::string& login, const std::string& access, const std::string& renew) {
|
unsigned int DB::MySQL::createSession(const std::string& login, const std::string& access, const std::string& renew) {
|
||||||
std::string l = login, a = access, r = renew;
|
std::string l = login, a = access, r = renew;
|
||||||
static std::string testingDevice("Testing...");
|
static std::string testingDevice("Testing...");
|
||||||
|
|
||||||
@ -281,7 +281,7 @@ unsigned int MySQL::createSession(const std::string& login, const std::string& a
|
|||||||
return lastInsertedId();
|
return lastInsertedId();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int MySQL::lastInsertedId() {
|
unsigned int DB::MySQL::lastInsertedId() {
|
||||||
MYSQL* con = &connection;
|
MYSQL* con = &connection;
|
||||||
int result = mysql_query(con, lastIdQuery);
|
int result = mysql_query(con, lastIdQuery);
|
||||||
|
|
||||||
|
@ -9,10 +9,11 @@
|
|||||||
|
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
|
|
||||||
#include "database/dbinterface.h"
|
#include "database/interface.h"
|
||||||
#include "utils/helpers.h"
|
#include "utils/helpers.h"
|
||||||
|
|
||||||
class MySQL : public DBInterface {
|
namespace DB {
|
||||||
|
class MySQL : public Interface {
|
||||||
class Statement;
|
class Statement;
|
||||||
class Transaction;
|
class Transaction;
|
||||||
|
|
||||||
@ -51,3 +52,4 @@ struct ResDeleter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
static uint64_t TIME_LENGTH = sizeof(MYSQL_TIME);
|
static uint64_t TIME_LENGTH = sizeof(MYSQL_TIME);
|
||||||
|
|
||||||
MySQL::Statement::Statement(MYSQL* connection, const char* statement):
|
DB::MySQL::Statement::Statement(MYSQL* connection, const char* statement):
|
||||||
stmt(mysql_stmt_init(connection)),
|
stmt(mysql_stmt_init(connection)),
|
||||||
param()
|
param()
|
||||||
{
|
{
|
||||||
@ -18,7 +18,7 @@ MySQL::Statement::Statement(MYSQL* connection, const char* statement):
|
|||||||
throw std::runtime_error(std::string("Error preparing statement: ") + mysql_stmt_error(stmt.get()));
|
throw std::runtime_error(std::string("Error preparing statement: ") + mysql_stmt_error(stmt.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::Statement::bind(void* value, enum_field_types type, bool usigned) {
|
void DB::MySQL::Statement::bind(void* value, enum_field_types type, bool usigned) {
|
||||||
MYSQL_BIND& result = param.emplace_back();
|
MYSQL_BIND& result = param.emplace_back();
|
||||||
std::memset(&result, 0, sizeof(result));
|
std::memset(&result, 0, sizeof(result));
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ void MySQL::Statement::bind(void* value, enum_field_types type, bool usigned) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::Statement::execute() {
|
void DB::MySQL::Statement::execute() {
|
||||||
MYSQL_STMT* raw = stmt.get();
|
MYSQL_STMT* raw = stmt.get();
|
||||||
int result = mysql_stmt_bind_param(raw, param.data());
|
int result = mysql_stmt_bind_param(raw, param.data());
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
@ -64,7 +64,7 @@ void MySQL::Statement::execute() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<std::any>> MySQL::Statement::fetchResult() {
|
std::vector<std::vector<std::any>> DB::MySQL::Statement::fetchResult() {
|
||||||
MYSQL_STMT* raw = stmt.get();
|
MYSQL_STMT* raw = stmt.get();
|
||||||
if (mysql_stmt_store_result(raw) != 0)
|
if (mysql_stmt_store_result(raw) != 0)
|
||||||
throw std::runtime_error(std::string("Error fetching statement result: ") + mysql_stmt_error(raw)); //TODO not sure if it's valid here
|
throw std::runtime_error(std::string("Error fetching statement result: ") + mysql_stmt_error(raw)); //TODO not sure if it's valid here
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "mysql.h"
|
#include "mysql.h"
|
||||||
|
|
||||||
|
namespace DB {
|
||||||
class MySQL::Statement {
|
class MySQL::Statement {
|
||||||
struct STMTDeleter {
|
struct STMTDeleter {
|
||||||
void operator () (MYSQL_STMT* stmt) {
|
void operator () (MYSQL_STMT* stmt) {
|
||||||
@ -27,3 +28,4 @@ private:
|
|||||||
std::unique_ptr<MYSQL_STMT, STMTDeleter> stmt;
|
std::unique_ptr<MYSQL_STMT, STMTDeleter> stmt;
|
||||||
std::vector<MYSQL_BIND> param;
|
std::vector<MYSQL_BIND> param;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "transaction.h"
|
#include "transaction.h"
|
||||||
|
|
||||||
MySQL::Transaction::Transaction(MYSQL* connection):
|
DB::MySQL::Transaction::Transaction(MYSQL* connection):
|
||||||
con(connection),
|
con(connection),
|
||||||
opened(false)
|
opened(false)
|
||||||
{
|
{
|
||||||
@ -13,12 +13,12 @@ MySQL::Transaction::Transaction(MYSQL* connection):
|
|||||||
opened = true;
|
opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MySQL::Transaction::~Transaction() {
|
DB::MySQL::Transaction::~Transaction() {
|
||||||
if (opened)
|
if (opened)
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::Transaction::commit() {
|
void DB::MySQL::Transaction::commit() {
|
||||||
if (mysql_commit(con) != 0)
|
if (mysql_commit(con) != 0)
|
||||||
throw std::runtime_error(std::string("Failed to commit transaction") + mysql_error(con));
|
throw std::runtime_error(std::string("Failed to commit transaction") + mysql_error(con));
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ void MySQL::Transaction::commit() {
|
|||||||
throw std::runtime_error(std::string("Failed to return autocommit") + mysql_error(con));
|
throw std::runtime_error(std::string("Failed to return autocommit") + mysql_error(con));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySQL::Transaction::abort() {
|
void DB::MySQL::Transaction::abort() {
|
||||||
opened = false;
|
opened = false;
|
||||||
if (mysql_rollback(con) != 0)
|
if (mysql_rollback(con) != 0)
|
||||||
throw std::runtime_error(std::string("Failed to rollback transaction") + mysql_error(con));
|
throw std::runtime_error(std::string("Failed to rollback transaction") + mysql_error(con));
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "mysql.h"
|
#include "mysql.h"
|
||||||
|
|
||||||
|
namespace DB {
|
||||||
class MySQL::Transaction {
|
class MySQL::Transaction {
|
||||||
public:
|
public:
|
||||||
Transaction(MYSQL* connection);
|
Transaction(MYSQL* connection);
|
||||||
@ -17,3 +18,4 @@ private:
|
|||||||
MYSQL* con;
|
MYSQL* con;
|
||||||
bool opened;
|
bool opened;
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
57
database/pool.cpp
Normal file
57
database/pool.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "pool.h"
|
||||||
|
|
||||||
|
DB::Pool::Pool (Private):
|
||||||
|
std::enable_shared_from_this<Pool>(),
|
||||||
|
mutex(),
|
||||||
|
conditional(),
|
||||||
|
interfaces()
|
||||||
|
{}
|
||||||
|
|
||||||
|
DB::Pool::~Pool () {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DB::Pool> DB::Pool::create () {
|
||||||
|
return std::make_shared<Pool>(Private());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DB::Pool::addInterfaces (
|
||||||
|
Interface::Type type,
|
||||||
|
std::size_t amount,
|
||||||
|
const std::string & login,
|
||||||
|
const std::string & password,
|
||||||
|
const std::string & database,
|
||||||
|
const std::string& path
|
||||||
|
) {
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
for (std::size_t i = 0; i < amount; ++i) {
|
||||||
|
const std::unique_ptr<Interface>& ref = interfaces.emplace(Interface::create(type));
|
||||||
|
ref->setCredentials(login, password);
|
||||||
|
ref->setDatabase(database);
|
||||||
|
ref->connect(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
conditional.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::Resource DB::Pool::request () {
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
while (interfaces.empty())
|
||||||
|
conditional.wait(lock);
|
||||||
|
|
||||||
|
std::unique_ptr<Interface> interface = std::move(interfaces.front());
|
||||||
|
interfaces.pop();
|
||||||
|
return Resource(std::move(interface), shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
void DB::Pool::free (std::unique_ptr<Interface> interface) {
|
||||||
|
std::unique_lock lock(mutex);
|
||||||
|
|
||||||
|
interfaces.push(std::move(interface));
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
conditional.notify_one();
|
||||||
|
}
|
47
database/pool.h
Normal file
47
database/pool.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include "interface.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
namespace DB {
|
||||||
|
class Pool : public std::enable_shared_from_this<Pool> {
|
||||||
|
struct Private {};
|
||||||
|
friend class Resource;
|
||||||
|
|
||||||
|
void free(std::unique_ptr<Interface> interface);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Pool(Private);
|
||||||
|
Pool(const Pool&) = delete;
|
||||||
|
Pool(Pool&&) = delete;
|
||||||
|
~Pool();
|
||||||
|
Pool& operator = (const Pool&) = delete;
|
||||||
|
Pool& operator = (Pool&&) = delete;
|
||||||
|
|
||||||
|
static std::shared_ptr<Pool> create();
|
||||||
|
Resource request();
|
||||||
|
void addInterfaces(
|
||||||
|
Interface::Type type,
|
||||||
|
std::size_t amount,
|
||||||
|
const std::string& login,
|
||||||
|
const std::string& password,
|
||||||
|
const std::string& database,
|
||||||
|
const std::string& path
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex;
|
||||||
|
std::condition_variable conditional;
|
||||||
|
std::queue<std::unique_ptr<Interface>> interfaces;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
38
database/resource.cpp
Normal file
38
database/resource.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#include "pool.h"
|
||||||
|
|
||||||
|
DB::Resource::Resource (
|
||||||
|
std::unique_ptr<Interface> interface,
|
||||||
|
std::weak_ptr<Pool> parent
|
||||||
|
):
|
||||||
|
parent(parent),
|
||||||
|
interface(std::move(interface))
|
||||||
|
{}
|
||||||
|
|
||||||
|
DB::Resource::Resource(Resource&& other):
|
||||||
|
parent(other.parent),
|
||||||
|
interface(std::move(other.interface))
|
||||||
|
{}
|
||||||
|
|
||||||
|
DB::Resource::~Resource() {
|
||||||
|
if (!interface)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (std::shared_ptr<Pool> p = parent.lock())
|
||||||
|
p->free(std::move(interface));
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::Resource& DB::Resource::operator = (Resource&& other) {
|
||||||
|
parent = other.parent;
|
||||||
|
interface = std::move(other.interface);
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::Interface* DB::Resource::operator -> () {
|
||||||
|
return interface.get();
|
||||||
|
}
|
31
database/resource.h
Normal file
31
database/resource.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 Yury Gubich <blue@macaw.me>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "interface.h"
|
||||||
|
|
||||||
|
namespace DB {
|
||||||
|
class Pool;
|
||||||
|
|
||||||
|
class Resource {
|
||||||
|
friend class Pool;
|
||||||
|
Resource(std::unique_ptr<Interface> interface, std::weak_ptr<Pool> parent);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Resource(const Resource&) = delete;
|
||||||
|
Resource(Resource&& other);
|
||||||
|
~Resource();
|
||||||
|
|
||||||
|
Resource& operator = (const Resource&) = delete;
|
||||||
|
Resource& operator = (Resource&& other);
|
||||||
|
|
||||||
|
Interface* operator -> ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr<Pool> parent;
|
||||||
|
std::unique_ptr<Interface> interface;
|
||||||
|
};
|
||||||
|
}
|
@ -32,7 +32,7 @@ void Handler::Login::handle(Request& request) {
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
try {
|
try {
|
||||||
success = server->validatePassword(login, password);
|
success = server->validatePassword(login, password);
|
||||||
} catch (const DBInterface::NoLogin& e) {
|
} catch (const DB::NoLogin& e) {
|
||||||
std::cerr << "Exception on logging in:\n\t" << e.what() << std::endl;
|
std::cerr << "Exception on logging in:\n\t" << e.what() << std::endl;
|
||||||
return error(request, Result::wrongCredentials, Response::Status::badRequest);
|
return error(request, Result::wrongCredentials, Response::Status::badRequest);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -35,7 +35,7 @@ void Handler::Register::handle(Request& request) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
server->registerAccount(login, password);
|
server->registerAccount(login, password);
|
||||||
} catch (const DBInterface::DuplicateLogin& e) {
|
} catch (const DB::DuplicateLogin& e) {
|
||||||
std::cerr << "Exception on registration:\n\t" << e.what() << std::endl;
|
std::cerr << "Exception on registration:\n\t" << e.what() << std::endl;
|
||||||
return error(request, Result::loginExists, Response::Status::conflict);
|
return error(request, Result::loginExists, Response::Status::conflict);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
set(HEADERS
|
set(HEADERS
|
||||||
request.h
|
request.h
|
||||||
|
redirect.h
|
||||||
|
|
||||||
redirectable.h
|
redirectable.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
request.cpp
|
request.cpp
|
||||||
|
redirect.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(pica PRIVATE ${SOURCES})
|
||||||
|
@ -9,5 +9,6 @@
|
|||||||
|
|
||||||
class Accepting {
|
class Accepting {
|
||||||
public:
|
public:
|
||||||
|
virtual ~Accepting() {};
|
||||||
virtual void accept(std::unique_ptr<Request> request) = 0;
|
virtual void accept(std::unique_ptr<Request> request) = 0;
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,11 @@
|
|||||||
#include "handler/login.h"
|
#include "handler/login.h"
|
||||||
|
|
||||||
constexpr const char* pepper = "well, not much of a secret, huh?";
|
constexpr const char* pepper = "well, not much of a secret, huh?";
|
||||||
|
constexpr const char* dbLogin = "pica";
|
||||||
|
constexpr const char* dbPassword = "pica";
|
||||||
|
constexpr const char* dbName = "pica";
|
||||||
|
constexpr const char* dbPath = "/run/mysqld/mysqld.sock";
|
||||||
|
constexpr uint8_t dbConnectionsCount = 4;
|
||||||
|
|
||||||
constexpr const char* randomChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
constexpr const char* randomChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
constexpr uint8_t saltSize = 16;
|
constexpr uint8_t saltSize = 16;
|
||||||
@ -28,18 +33,22 @@ Server::Server():
|
|||||||
requestCount(0),
|
requestCount(0),
|
||||||
serverName(std::nullopt),
|
serverName(std::nullopt),
|
||||||
router(),
|
router(),
|
||||||
db(),
|
pool(DB::Pool::create()),
|
||||||
sessions()
|
sessions()
|
||||||
{
|
{
|
||||||
std::cout << "Startig pica..." << std::endl;
|
std::cout << "Startig pica..." << std::endl;
|
||||||
|
|
||||||
db = DBInterface::create(DBInterface::Type::mysql);
|
|
||||||
std::cout << "Database type: MySQL" << std::endl;
|
std::cout << "Database type: MySQL" << std::endl;
|
||||||
|
pool->addInterfaces(
|
||||||
|
DB::Interface::Type::mysql,
|
||||||
|
dbConnectionsCount,
|
||||||
|
dbLogin,
|
||||||
|
dbPassword,
|
||||||
|
dbName,
|
||||||
|
dbPath
|
||||||
|
);
|
||||||
|
|
||||||
db->setCredentials("pica", "pica");
|
DB::Resource db = pool->request();
|
||||||
db->setDatabase("pica");
|
|
||||||
|
|
||||||
db->connect("/run/mysqld/mysqld.sock");
|
|
||||||
db->migrate(currentDbVesion);
|
db->migrate(currentDbVesion);
|
||||||
|
|
||||||
router.addRoute(std::make_unique<Handler::Info>());
|
router.addRoute(std::make_unique<Handler::Info>());
|
||||||
@ -112,10 +121,12 @@ unsigned int Server::registerAccount(const std::string& login, const std::string
|
|||||||
if (result != ARGON2_OK)
|
if (result != ARGON2_OK)
|
||||||
throw std::runtime_error(std::string("Hashing failed: ") + argon2_error_message(result));
|
throw std::runtime_error(std::string("Hashing failed: ") + argon2_error_message(result));
|
||||||
|
|
||||||
|
DB::Resource db = pool->request();
|
||||||
return db->registerAccount(login, hash);
|
return db->registerAccount(login, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::validatePassword(const std::string& login, const std::string& password) {
|
bool Server::validatePassword(const std::string& login, const std::string& password) {
|
||||||
|
DB::Resource db = pool->request();
|
||||||
std::string hash = db->getAccountHash(login);
|
std::string hash = db->getAccountHash(login);
|
||||||
|
|
||||||
std::string spiced = password + pepper;
|
std::string spiced = password + pepper;
|
||||||
@ -139,9 +150,10 @@ Session& Server::openSession(const std::string& login) {
|
|||||||
try {
|
try {
|
||||||
accessToken = generateRandomString(32);
|
accessToken = generateRandomString(32);
|
||||||
renewToken = generateRandomString(32);
|
renewToken = generateRandomString(32);
|
||||||
|
DB::Resource db = pool->request();
|
||||||
sessionId = db->createSession(login, accessToken, renewToken);
|
sessionId = db->createSession(login, accessToken, renewToken);
|
||||||
break;
|
break;
|
||||||
} catch (const DBInterface::Duplicate& e) {
|
} catch (const DB::Duplicate& e) {
|
||||||
std::cout << "Duplicate on creating session, trying again with different tokens";
|
std::cout << "Duplicate on creating session, trying again with different tokens";
|
||||||
}
|
}
|
||||||
} while (--counter != 0);
|
} while (--counter != 0);
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include "response/response.h"
|
#include "response/response.h"
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "database/dbinterface.h"
|
#include "database/pool.h"
|
||||||
#include "utils/helpers.h"
|
#include "utils/helpers.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@ -49,6 +49,6 @@ private:
|
|||||||
uint64_t requestCount;
|
uint64_t requestCount;
|
||||||
std::optional<std::string> serverName;
|
std::optional<std::string> serverName;
|
||||||
Router router;
|
Router router;
|
||||||
std::unique_ptr<DBInterface> db;
|
std::shared_ptr<DB::Pool> pool;
|
||||||
Sessions sessions;
|
Sessions sessions;
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
class Session : public Accepting {
|
class Session : public Accepting {
|
||||||
public:
|
public:
|
||||||
Session(unsigned int id, const std::string& access, const std::string& renew);
|
Session(unsigned int id, const std::string& access, const std::string& renew);
|
||||||
|
Session(const Session&) = delete;
|
||||||
|
Session(Session&& other);
|
||||||
|
Session& operator = (const Session&) = delete;
|
||||||
|
Session& operator = (Session&& other);
|
||||||
|
|
||||||
std::string getAccessToken() const;
|
std::string getAccessToken() const;
|
||||||
std::string getRenewToken() const;
|
std::string getRenewToken() const;
|
||||||
|
Loading…
Reference in New Issue
Block a user