From a2ab75b18d44c454936ca49a850891c81f383c86 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Tue, 20 Oct 2020 23:18:10 +0200 Subject: [PATCH] Initial version of a rest server This is more of a proof of concept than the actual feature. WiP --- orotool.conf | 3 ++ src/app_config.cpp | 9 ++++ src/app_config.hpp | 1 + src/config.hpp.in | 1 + src/main.cpp | 5 ++ src/meson.build | 7 +++ src/oro/datatypes.cpp | 8 +++ src/oro/datatypes.hpp | 1 + src/timer_base.cpp | 4 +- src/timer_oro_api.cpp | 2 +- src/webserver/private/dateconv.cpp | 34 +++++++++++++ src/webserver/private/dateconv.hpp | 25 ++++++++++ src/webserver/rest_server.cpp | 79 ++++++++++++++++++++++++++++++ src/webserver/rest_server.hpp | 34 +++++++++++++ 14 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 src/webserver/private/dateconv.cpp create mode 100644 src/webserver/private/dateconv.hpp create mode 100644 src/webserver/rest_server.cpp create mode 100644 src/webserver/rest_server.hpp diff --git a/orotool.conf b/orotool.conf index 14cd9f0..33cc091 100644 --- a/orotool.conf +++ b/orotool.conf @@ -16,3 +16,6 @@ items=604800 icons=604800 shops=120 creators=400 + +[webserver] +enabled=yes diff --git a/src/app_config.cpp b/src/app_config.cpp index 2e04801..90ff4cc 100644 --- a/src/app_config.cpp +++ b/src/app_config.cpp @@ -54,6 +54,10 @@ namespace { constexpr const char g_store_raw_json[] = "store_raw_json"; constexpr const char g_store_raw_json_def[] = "false"; + constexpr const char g_enable_webserver_sect[] = "webserver"; + constexpr const char g_enable_webserver[] = "enabled"; + constexpr const char g_enable_webserver_def[] = "no"; + constexpr const char g_json_store_mode_sect[] = "options"; constexpr const char g_json_store_mode[] = "json_store_mode"; constexpr const char g_json_store_mode_def[] = "plain"; @@ -197,6 +201,11 @@ bool AppConfig::store_raw_json() const { return to_bool(val); } +bool AppConfig::enable_webserver() const { + std::string_view val = value_ifp(m_ini, g_enable_webserver_sect, g_enable_webserver, g_enable_webserver_def, false); + return to_bool(val); +} + std::size_t AppConfig::items_timeout() const { std::string_view val = value_ifp(m_ini, g_items_time_sect, g_items_time, g_items_time_def, false); return std::max(dhandy::int_conv(val), g_min_update_timeout); diff --git a/src/app_config.hpp b/src/app_config.hpp index 87d8f53..edbd520 100644 --- a/src/app_config.hpp +++ b/src/app_config.hpp @@ -35,6 +35,7 @@ public: std::size_t fetch_extra_delay() const; std::string_view backend() const; bool store_raw_json() const; + bool enable_webserver() const; std::size_t items_timeout() const; std::size_t icons_timeout() const; diff --git a/src/config.hpp.in b/src/config.hpp.in index 6544a4f..bff631a 100644 --- a/src/config.hpp.in +++ b/src/config.hpp.in @@ -30,6 +30,7 @@ constexpr const unsigned short int g_version_minor = @PROJECT_VERSION_MINOR@; constexpr const unsigned short int g_version_patch = @PROJECT_VERSION_PATCH@; #mesondefine OROTOOL_WITH_LZMA +#mesondefine OROTOOL_WITH_WEBSERVER #mesondefine OROTOOL_WITH_SQLITE diff --git a/src/main.cpp b/src/main.cpp index 519bff8..26de383 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,9 @@ #include "app_config.hpp" #include "duckhandy/int_conv.hpp" #include "duckhandy/string_bt.hpp" +#if defined(OROTOOL_WITH_WEBSERVER) +# include "webserver/rest_server.hpp" +#endif #if !defined(NDEBUG) # include #endif @@ -97,6 +100,8 @@ int main(int argc, char* argv[]) { std::unique_ptr db(oro::OriginsDB::make(app_conf.backend(), app_conf.db_path())); + duck::RestServer server(8171); + duck::test(oro_api.get(), db.get(), app_conf); } #if defined(OROTOOL_WITH_RESTCCPP) diff --git a/src/meson.build b/src/meson.build index de643d7..c7c6e1a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -28,6 +28,7 @@ civetweb_dep = dependency('civetweb', version: '>=1.12', 'civetweb_enable_server_executable=false', 'civetweb_enable_cgi=false', 'civetweb_enable_caching=true', + 'default_library=static', ], ) @@ -77,6 +78,7 @@ conf.set('OROTOOL_WITH_SQLITE', sqlitecpp_dep.found()) conf.set('OROTOOL_WITH_LZMA', lzma_dep.found()) conf.set('OROTOOL_WITH_NAP', get_option('rest_lib') == 'nap') conf.set('OROTOOL_WITH_RESTCCPP', get_option('rest_lib') == 'restc-cpp') +conf.set('OROTOOL_WITH_WEBSERVER', civetweb_dep.found()) project_config_file = configure_file( input: 'config.hpp.in', output: meson.project_name() + '_config.hpp', @@ -125,6 +127,10 @@ if lzma_dep.found() optional_sources += ['lzma.cpp'] endif +if civetweb_dep.found() + optional_sources += ['webserver/rest_server.cpp'] +endif + executable(meson.project_name(), 'main.cpp', 'ini_file.cpp', @@ -152,6 +158,7 @@ executable(meson.project_name(), optional_sources, project_config_file, 'eventia_thread_pool.cpp', + 'webserver/private/dateconv.cpp', install: true, dependencies: lib_deps, include_directories: [ diff --git a/src/oro/datatypes.cpp b/src/oro/datatypes.cpp index b2c66f4..c9ccc5d 100644 --- a/src/oro/datatypes.cpp +++ b/src/oro/datatypes.cpp @@ -27,6 +27,14 @@ namespace oro { return *this; } + Timestamp Timestamp::now() { + return Timestamp{ + std::chrono::time_point_cast( + std::chrono::system_clock::now() + ) + }; + } + std::ostream& operator<<(std::ostream& os, const Timestamp& ts) { using date::operator<<; os << ts.ts; diff --git a/src/oro/datatypes.hpp b/src/oro/datatypes.hpp index 8a18b09..bb8b61b 100644 --- a/src/oro/datatypes.hpp +++ b/src/oro/datatypes.hpp @@ -27,6 +27,7 @@ namespace oro { struct Timestamp { Timestamp& operator= (std::string&& str); operator timestamp_t() const { return ts; } + static Timestamp now(); timestamp_t ts; }; diff --git a/src/timer_base.cpp b/src/timer_base.cpp index 7a61c47..81c7e82 100644 --- a/src/timer_base.cpp +++ b/src/timer_base.cpp @@ -47,7 +47,7 @@ namespace { oro::Timestamp calc_next_update (const oro::Header& header, double min_wait, double extra) { oro::Timestamp ret; ret.ts = - std::chrono::time_point_cast(std::chrono::system_clock::now()) + + oro::Timestamp::now().ts + std::chrono::seconds(time_interval(header, min_wait, extra)); return ret; } @@ -114,7 +114,7 @@ oro::OriginsDB& TimerBase::db() { void TimerBase::reset_db_access_time (oro::DBOperation op) { oro::Timestamp ts; ts.ts = - std::chrono::time_point_cast(std::chrono::system_clock::now()) + + oro::Timestamp::now().ts + std::chrono::seconds(static_cast(m_min_wait)); db().update_access_time(ts, op); } diff --git a/src/timer_oro_api.cpp b/src/timer_oro_api.cpp index 78453c0..e921be3 100644 --- a/src/timer_oro_api.cpp +++ b/src/timer_oro_api.cpp @@ -136,7 +136,7 @@ inline void TimerOroApi::fetch_data (oro::SourceFormat store_mode) { m_retries_left = error_retries(); if (429 == status_code) { oro::Header head; - head.date.ts = std::chrono::time_point_cast(std::chrono::system_clock::now()); + head.date.ts = oro::Timestamp::now().ts; head.rate_limit = 1; head.rate_limit_remaining = 0; head.rate_limit_reset = 600; diff --git a/src/webserver/private/dateconv.cpp b/src/webserver/private/dateconv.cpp new file mode 100644 index 0000000..d3a4d67 --- /dev/null +++ b/src/webserver/private/dateconv.cpp @@ -0,0 +1,34 @@ +/* Copyright 2020, Michele Santullo + * This file is part of orotool. + * + * Orotool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orotool 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orotool. If not, see . + */ + +#include "dateconv.hpp" + +#define HAS_UNCAUGHT_EXCEPTIONS 1 +#include "date/date.h" + +namespace duck { + +std::string to_header_string (const oro::Timestamp& ts) { + using date::operator<<; + using std::chrono::seconds; + using date::floor; + using date::format; + + return format("%a, %d %b %Y %T %Z", floor(ts.ts)); +} + +} //namespace duck diff --git a/src/webserver/private/dateconv.hpp b/src/webserver/private/dateconv.hpp new file mode 100644 index 0000000..0f6c859 --- /dev/null +++ b/src/webserver/private/dateconv.hpp @@ -0,0 +1,25 @@ +/* Copyright 2020, Michele Santullo + * This file is part of orotool. + * + * Orotool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orotool 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orotool. If not, see . + */ + +#pragma once + +#include "oro/datatypes.hpp" +#include + +namespace duck { +std::string to_header_string (const oro::Timestamp& ts); +} //namespace duck diff --git a/src/webserver/rest_server.cpp b/src/webserver/rest_server.cpp new file mode 100644 index 0000000..479141c --- /dev/null +++ b/src/webserver/rest_server.cpp @@ -0,0 +1,79 @@ +/* Copyright 2020, Michele Santullo + * This file is part of orotool. + * + * Orotool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orotool 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orotool. If not, see . + */ + +#include "rest_server.hpp" +#include "CivetServer.h" +#include "duckhandy/int_conv.hpp" +#include "private/dateconv.hpp" + +namespace duck { +namespace { +class V1ItemsHandler : public CivetHandler { +public: + bool handleGet (CivetServer* server, struct mg_connection* conn) override { + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json; charset=utf-8\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "\r\n", + to_header_string(oro::Timestamp::now()).c_str() + ); + mg_printf(conn, "Hi this is the items handler\r\n"); + return true; + } +}; + +void init_civetweb_ifn() { + static bool initialised = false; + if (not initialised) { + initialised = true; + if (mg_check_feature(2)) + mg_init_library(MG_FEATURES_SSL); + else + mg_init_library(0); + } +} +} //unnamed namespace + +struct RestServer::LocalData { + explicit LocalData (const char* options[]) : + server(options) + { + server.addHandler("/v1/items", v1_items_handler); + } + + V1ItemsHandler v1_items_handler; + CivetServer server; +}; + +RestServer::RestServer (uint16_t port) { + init_civetweb_ifn(); + + const std::string port_str = dhandy::int_conv(port); + const char* options[] = { + "decode_url", "yes", + "listening_ports", port_str.c_str(), + "request_timeout_ms", "10000", + nullptr + }; + + m_local = std::make_unique(options); +} + +RestServer::~RestServer() noexcept = default; +} //namespace duck diff --git a/src/webserver/rest_server.hpp b/src/webserver/rest_server.hpp new file mode 100644 index 0000000..cc76844 --- /dev/null +++ b/src/webserver/rest_server.hpp @@ -0,0 +1,34 @@ +/* Copyright 2020, Michele Santullo + * This file is part of orotool. + * + * Orotool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Orotool 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Orotool. If not, see . + */ + +#pragma once + +#include +#include + +namespace duck { +class RestServer { +public: + RestServer (uint16_t port); + ~RestServer() noexcept; + +private: + struct LocalData; + + std::unique_ptr m_local; +}; +} //namespace duck