Compare commits
No commits in common. "6ca40c2b8afdf5c444a5f82fde6f23120fe8d375" and "b3801e5201f1c7946decd612b9e47ba1da84ec8c" have entirely different histories.
6ca40c2b8a
...
b3801e5201
@ -4,8 +4,7 @@ project(pica
|
|||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
cmake_policy(SET CMP0076 NEW) #allow adding sources from subdir
|
cmake_policy(SET CMP0076 NEW)
|
||||||
cmake_policy(SET CMP0079 NEW) #allow linking from subdirs
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@ -21,7 +20,6 @@ add_subdirectory(server)
|
|||||||
add_subdirectory(request)
|
add_subdirectory(request)
|
||||||
add_subdirectory(response)
|
add_subdirectory(response)
|
||||||
add_subdirectory(stream)
|
add_subdirectory(stream)
|
||||||
add_subdirectory(database)
|
|
||||||
|
|
||||||
target_link_libraries(pica PRIVATE
|
target_link_libraries(pica PRIVATE
|
||||||
FCGI::FCGI
|
FCGI::FCGI
|
||||||
|
12
README.md
12
README.md
@ -39,18 +39,6 @@ Incude this file from apache config file (in my case it's `/etc/httpd/conf/httpd
|
|||||||
Include conf/extra/fcgi-pica.conf
|
Include conf/extra/fcgi-pica.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
Also you need to have 2 modules enabled `proxy` and `proxy_fcgi`, usually you need to uncomment following lines in `/etc/httpd/conf/httpd.conf`
|
|
||||||
```
|
|
||||||
LoadModule proxy_module modules/mod_proxy.so
|
|
||||||
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you use debian-like distro (ubuntu) you should use `a2enmod` command instead:
|
|
||||||
```
|
|
||||||
# a2enmod proxy_fcgi
|
|
||||||
```
|
|
||||||
This should enable both modules, because module `proxy_fcgi` has module `proxy` as a dependency.
|
|
||||||
|
|
||||||
Start apache
|
Start apache
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
find_library(MariaDB_CLIENT_LIBRARIES mysqlclient NAMES mariadbclient)
|
|
||||||
find_path(MariaDB_INCLUDE_DIR mysql/mysql.h)
|
|
||||||
|
|
||||||
if (MariaDB_CLIENT_LIBRARIES AND MariaDB_INCLUDE_DIR)
|
|
||||||
set(MariaDB_FOUND TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (MariaDB_FOUND)
|
|
||||||
add_library(MariaDB::client SHARED IMPORTED)
|
|
||||||
set_target_properties(MariaDB::client PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${MariaDB_CLIENT_LIBRARIES}"
|
|
||||||
INTERFACE_LINK_LIBRARIES "${MariaDB_CLIENT_LIBRARIES}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES ${MariaDB_INCLUDE_DIR}/mysql
|
|
||||||
)
|
|
||||||
|
|
||||||
if (NOT MariaDB_FIND_QUIETLY)
|
|
||||||
message(STATUS "Found MariaDB includes: ${MariaDB_INCLUDE_DIR}/mysql")
|
|
||||||
message(STATUS "Found MariaDB client library: ${MariaDB_CLIENT_LIBRARIES}")
|
|
||||||
endif ()
|
|
||||||
else ()
|
|
||||||
if (MariaDB_FIND_REQUIRED)
|
|
||||||
message(FATAL_ERROR "Could NOT find MariaDB development files")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
|||||||
set(HEADERS
|
|
||||||
dbinterface.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SOURCES
|
|
||||||
dbinterface.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
|
||||||
|
|
||||||
add_subdirectory(mysql)
|
|
@ -1,23 +0,0 @@
|
|||||||
#include "dbinterface.h"
|
|
||||||
|
|
||||||
#include "mysql/mysql.h"
|
|
||||||
|
|
||||||
DBInterface::DBInterface(Type type):
|
|
||||||
type(type),
|
|
||||||
state(State::disconnected)
|
|
||||||
{}
|
|
||||||
|
|
||||||
DBInterface::~DBInterface() {}
|
|
||||||
|
|
||||||
std::unique_ptr<DBInterface> DBInterface::create(Type type) {
|
|
||||||
switch (type) {
|
|
||||||
case Type::mysql:
|
|
||||||
return std::make_unique<MySQL>();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Unexpected database type: " + std::to_string((uint8_t)type));
|
|
||||||
}
|
|
||||||
|
|
||||||
DBInterface::State DBInterface::currentState() const {
|
|
||||||
return state;
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class DBInterface {
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
mysql
|
|
||||||
};
|
|
||||||
enum class State {
|
|
||||||
disconnected,
|
|
||||||
connecting,
|
|
||||||
connected
|
|
||||||
};
|
|
||||||
static std::unique_ptr<DBInterface> create(Type type);
|
|
||||||
|
|
||||||
virtual ~DBInterface();
|
|
||||||
|
|
||||||
State currentState() const;
|
|
||||||
|
|
||||||
const Type type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void connect(const std::string& path) = 0;
|
|
||||||
virtual void disconnect() = 0;
|
|
||||||
virtual void setDatabase(const std::string& newDatabase) = 0;
|
|
||||||
virtual void setCredentials(const std::string& login, const std::string& password) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
DBInterface(Type type);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
State state;
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
set(HEADERS
|
|
||||||
mysql.h
|
|
||||||
)
|
|
||||||
|
|
||||||
set(SOURCES
|
|
||||||
mysql.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
find_package(MariaDB REQUIRED)
|
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
|
||||||
|
|
||||||
target_link_libraries(pica PRIVATE MariaDB::client)
|
|
@ -1,85 +0,0 @@
|
|||||||
#include "mysql.h"
|
|
||||||
|
|
||||||
MySQL::MySQL():
|
|
||||||
DBInterface(Type::mysql),
|
|
||||||
connection(),
|
|
||||||
login(),
|
|
||||||
password(),
|
|
||||||
database()
|
|
||||||
{
|
|
||||||
mysql_init(&connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
MySQL::~MySQL() {
|
|
||||||
mysql_close(&connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MySQL::connect(const std::string& path) {
|
|
||||||
if (state != State::disconnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MYSQL* con = &connection;
|
|
||||||
MYSQL* res = mysql_real_connect(
|
|
||||||
con,
|
|
||||||
NULL,
|
|
||||||
login.c_str(),
|
|
||||||
password.c_str(),
|
|
||||||
database.empty() ? NULL : database.c_str(),
|
|
||||||
0,
|
|
||||||
path.c_str(),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res != con)
|
|
||||||
throw std::runtime_error(std::string("Error changing connecting: ") + mysql_error(con));
|
|
||||||
|
|
||||||
state = State::connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySQL::setCredentials(const std::string& login, const std::string& password) {
|
|
||||||
if (MySQL::login == login && MySQL::password == password)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MySQL::login = login;
|
|
||||||
MySQL::password = password;
|
|
||||||
|
|
||||||
if (state == State::disconnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MYSQL* con = &connection;
|
|
||||||
int result = mysql_change_user(
|
|
||||||
con,
|
|
||||||
login.c_str(),
|
|
||||||
password.c_str(),
|
|
||||||
database.empty() ? NULL : database.c_str()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
throw std::runtime_error(std::string("Error changing credetials: ") + mysql_error(con));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySQL::setDatabase(const std::string& database) {
|
|
||||||
if (MySQL::database == database)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MySQL::database = database;
|
|
||||||
|
|
||||||
if (state == State::disconnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MYSQL* con = &connection;
|
|
||||||
int result = mysql_select_db(con, database.c_str());
|
|
||||||
|
|
||||||
if (result != 0)
|
|
||||||
throw std::runtime_error(std::string("Error changing db: ") + mysql_error(con));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySQL::disconnect() {
|
|
||||||
if (state == State::disconnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
MYSQL* con = &connection;
|
|
||||||
mysql_close(con);
|
|
||||||
mysql_init(con); //this is ridiculous!
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <mysql.h>
|
|
||||||
|
|
||||||
#include "database/dbinterface.h"
|
|
||||||
|
|
||||||
class MySQL : public DBInterface {
|
|
||||||
public:
|
|
||||||
MySQL();
|
|
||||||
~MySQL() override;
|
|
||||||
|
|
||||||
void connect(const std::string& path) override;
|
|
||||||
void disconnect() override;
|
|
||||||
void setCredentials(const std::string& login, const std::string& password) override;
|
|
||||||
void setDatabase(const std::string& database) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
MYSQL connection;
|
|
||||||
std::string login;
|
|
||||||
std::string password;
|
|
||||||
std::string database;
|
|
||||||
};
|
|
@ -6,13 +6,6 @@ constexpr static const char* REQUEST_METHOD("REQUEST_METHOD");
|
|||||||
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
|
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
|
||||||
constexpr static const char* SERVER_NAME("SERVER_NAME");
|
constexpr static const char* SERVER_NAME("SERVER_NAME");
|
||||||
|
|
||||||
// constexpr static const char* REQUEST_URI("REQUEST_URI");
|
|
||||||
//
|
|
||||||
// constexpr static const char* DOCUMENT_URI("DOCUMENT_URI");
|
|
||||||
// constexpr static const char* DOCUMENT_ROOT("DOCUMENT_ROOT");
|
|
||||||
//
|
|
||||||
// constexpr static const char* SCRIPT_NAME("SCRIPT_NAME");
|
|
||||||
|
|
||||||
Request::Request ():
|
Request::Request ():
|
||||||
state(State::initial),
|
state(State::initial),
|
||||||
raw()
|
raw()
|
||||||
|
@ -34,8 +34,7 @@ void Response::replyTo(Request& request) const {
|
|||||||
if (!body.empty())
|
if (!body.empty())
|
||||||
out << '\n'
|
out << '\n'
|
||||||
<< contentTypes[static_cast<uint8_t>(type)]
|
<< contentTypes[static_cast<uint8_t>(type)]
|
||||||
<< '\n'
|
<< '\n' << '\n'
|
||||||
<< '\n'
|
|
||||||
<< body;
|
<< body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ void Router::addRoute(const std::string& path, const Handler& handler) {
|
|||||||
std::cerr << "could'not add route " + path + " to the routing table";
|
std::cerr << "could'not add route " + path + " to the routing table";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Router::route(const std::string& path, std::unique_ptr<Request> request, Server* server) {
|
void Router::route(const std::string& path, std::unique_ptr<Request> request) {
|
||||||
auto itr = table.find(path);
|
auto itr = table.find(path);
|
||||||
if (itr == table.end())
|
if (itr == table.end())
|
||||||
return handleNotFound(path, std::move(request));
|
return handleNotFound(path, std::move(request));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool result = itr->second(request.get(), server);
|
bool result = itr->second(request.get());
|
||||||
if (!result)
|
if (!result)
|
||||||
handleInternalError(std::runtime_error("handler failed to handle the request"), std::move(request));
|
handleInternalError(std::runtime_error("handler failed to handle the request"), std::move(request));
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
@ -8,16 +8,14 @@
|
|||||||
#include "request/request.h"
|
#include "request/request.h"
|
||||||
#include "response/response.h"
|
#include "response/response.h"
|
||||||
|
|
||||||
class Server;
|
|
||||||
|
|
||||||
class Router {
|
class Router {
|
||||||
public:
|
public:
|
||||||
using Handler = std::function<bool(Request*, Server*)>;
|
using Handler = std::function<bool(Request*)>;
|
||||||
|
|
||||||
Router();
|
Router();
|
||||||
|
|
||||||
void addRoute(const std::string& path, const Handler& handler);
|
void addRoute(const std::string& path, const Handler& handler);
|
||||||
void route(const std::string& path, std::unique_ptr<Request> request, Server* server);
|
void route(const std::string& path, std::unique_ptr<Request> request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleNotFound(const std::string& path, std::unique_ptr<Request> request);
|
void handleNotFound(const std::string& path, std::unique_ptr<Request> request);
|
||||||
|
@ -1,27 +1,19 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
constexpr static const char* REQUEST_URI("REQUEST_URI");
|
||||||
|
|
||||||
|
constexpr static const char* DOCUMENT_URI("DOCUMENT_URI");
|
||||||
|
constexpr static const char* DOCUMENT_ROOT("DOCUMENT_ROOT");
|
||||||
|
|
||||||
|
constexpr static const char* SCRIPT_NAME("SCRIPT_NAME");
|
||||||
|
constexpr static const char* SCRIPT_FILENAME("SCRIPT_FILENAME");
|
||||||
|
|
||||||
Server::Server():
|
Server::Server():
|
||||||
terminating(false),
|
terminating(false),
|
||||||
requestCount(0),
|
requestCount(0),
|
||||||
serverName(std::nullopt),
|
serverName(std::nullopt),
|
||||||
router(),
|
router()
|
||||||
db()
|
|
||||||
{
|
{
|
||||||
std::cout << "Startig pica..." << std::endl;
|
|
||||||
|
|
||||||
db = DBInterface::create(DBInterface::Type::mysql);
|
|
||||||
std::cout << "Database type: MySQL" << std::endl;
|
|
||||||
|
|
||||||
db->setCredentials("pica", "pica");
|
|
||||||
db->setDatabase("pica");
|
|
||||||
|
|
||||||
try {
|
|
||||||
db->connect("/run/mysqld/mysqld.sock");
|
|
||||||
std::cout << "Successfully connected to the database" << std::endl;
|
|
||||||
} catch (const std::runtime_error& e) {
|
|
||||||
std::cerr << "Couldn't connect to the database: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
router.addRoute("info", Server::info);
|
router.addRoute("info", Server::info);
|
||||||
router.addRoute("env", Server::printEnvironment);
|
router.addRoute("env", Server::printEnvironment);
|
||||||
}
|
}
|
||||||
@ -62,7 +54,7 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
std::string path = request->getPath(serverName.value());
|
std::string path = request->getPath(serverName.value());
|
||||||
router.route(path.data(), std::move(request), this);
|
router.route(path.data(), std::move(request));
|
||||||
} catch (const std::exception e) {
|
} catch (const std::exception e) {
|
||||||
Response error(Response::Status::internalError);
|
Response error(Response::Status::internalError);
|
||||||
error.setBody(std::string(e.what()));
|
error.setBody(std::string(e.what()));
|
||||||
@ -70,8 +62,7 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::printEnvironment(Request* request, Server* server) {
|
bool Server::printEnvironment(Request* request) {
|
||||||
(void)server;
|
|
||||||
nlohmann::json body = nlohmann::json::object();
|
nlohmann::json body = nlohmann::json::object();
|
||||||
request->printEnvironment(body);
|
request->printEnvironment(body);
|
||||||
|
|
||||||
@ -82,8 +73,7 @@ bool Server::printEnvironment(Request* request, Server* server) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server::info(Request* request, Server* server) {
|
bool Server::info(Request* request) {
|
||||||
(void)server;
|
|
||||||
Response res;
|
Response res;
|
||||||
nlohmann::json body = nlohmann::json::object();
|
nlohmann::json body = nlohmann::json::object();
|
||||||
body["type"] = "Pica";
|
body["type"] = "Pica";
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include "request/request.h"
|
#include "request/request.h"
|
||||||
#include "response/response.h"
|
#include "response/response.h"
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
#include "database/dbinterface.h"
|
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
public:
|
public:
|
||||||
@ -29,13 +28,12 @@ public:
|
|||||||
private:
|
private:
|
||||||
void handleRequest(std::unique_ptr<Request> request);
|
void handleRequest(std::unique_ptr<Request> request);
|
||||||
|
|
||||||
static bool info(Request* request, Server* server);
|
static bool info(Request* request);
|
||||||
static bool printEnvironment(Request* request, Server* server);
|
static bool printEnvironment(Request* request);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool terminating;
|
bool terminating;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user