drogonCMS-cmake #15

Merged
sharpetronics merged 2 commits from drogonCMS-cmake into master 2024-01-22 14:47:05 -05:00
24 changed files with 416 additions and 57 deletions

5
.gitignore vendored
View File

@ -68,8 +68,9 @@ m4/lt~obsolete.m4
/drogon_dashboard
/DrogonCMS
vendors/drogon/local-api-server/local-drogon-api-server
vendors/drogon/local-api-server/test/local-server_test
vendors/local-api-server/local-drogon-api-server
vendors/local-api-server/test/local-server_test
vendors/simple-reverse-proxy/simple-reverse-proxy
# Generated Makefile
# (meta build system like autotools,

View File

@ -30,7 +30,7 @@ cmake_minimum_required(VERSION 3.1...3.28)
# If you set any CMAKE_ variables, that can go here.
# (But usually don't do this, except maybe for C++ standard)
set(CMAKE_PROJECT_DESCRIPTION "A GTK / Drogon CMS")
set(CMAKE_PROJECT_DESCRIPTION "A GTK Drogon CMS")
set(CMAKE_PROJECT_HOMEPAGE_URL "https://github.com/odinzu/drogoncms")
set(CMAKE_C_STANDARD 11)
@ -116,7 +116,8 @@ add_definitions(${GTK4_CFLAGS_OTHER} ${ADW_CFLAGS_OTHER})
add_executable(DrogonCMS src/base.c)
# Include drogon source code locally
add_subdirectory(vendors/drogon/local-api-server/)
add_subdirectory(vendors/local-api-server/)
add_subdirectory(vendors/simple-reverse-proxy/)
# This is a "default" library, and will match the *** variable setting.
# Other common choices are STATIC, SHARED, and MODULE

View File

@ -1,53 +0,0 @@
## A Drogon API + optional Proxy Local Web Server
This software creates a secure localalized Drogon API application.
### License
* GPL-3.0
### Developer Dependency Requirements
* CLI Experience
*Note: This has been built and tested on Arch Linux*
### Manual Steps to build Drogon API from Source code
0. `nano ./config.yaml`
1. The `build/` folder is where the server will be compiled and served. `cd build/`
2. `cmake ..`
3. `make`
4. `./local-drogon-api-server`
5. Goto Web Browser and view the local API web server
`localhost:5555/`
## Development & Contributing
### Requirements
* A Computer
* CMake
* Update the ./config.yaml or .json file
### Recommendations
* A VPS Provider
## Authors
* SharpeTronics, LLC
* oDinZu WenKi(Charles)
## Financial Support & Donations
oDinZu WenKi(Charles) https://liberapay.com/oDinZu/
* Liberapay is a recurrent donations platform
* Run by a non-profit organization
* Source code is public
* No commission fee
* ~5% payment processing fee
## Learning Resources
* CMake Documentation https://cmake.org/cmake/help/book/mastering-cmake/index.html
* Drogon Web Server https://github.com/drogonframework/drogon
*Notice: We focus implementation with the C programming language and only add various features of C ++ code.*

View File

@ -1,3 +1,4 @@
// https://github.com/drogonframework/drogon/blob/master/examples/jsonstore/README.md
#include <drogon/drogon.h>
using namespace drogon;

36
vendors/simple-reverse-proxy/.gitignore vendored Normal file
View File

@ -0,0 +1,36 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
build
cmake-build-debug
.idea

View File

@ -0,0 +1,56 @@
cmake_minimum_required (VERSION 3.5)
project(simple_reverse_proxy CXX)
include(CheckIncludeFileCXX)
check_include_file_cxx(any HAS_ANY)
check_include_file_cxx(string_view HAS_STRING_VIEW)
if(HAS_ANY AND HAS_STRING_VIEW)
set(CMAKE_CXX_STANDARD 17)
else()
set(CMAKE_CXX_STANDARD 14)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(simple_reverse_proxy main.cc)
##########
# If you include the drogon source code locally in your project, use this method to add drogon
# add_subdirectory(drogon)
# target_link_libraries(simple_reverse_proxy PRIVATE drogon)
##########
find_package(Drogon CONFIG REQUIRED)
target_link_libraries(simple_reverse_proxy PRIVATE Drogon::Drogon)
if(CMAKE_CXX_STANDARD LESS 17)
#With C++14, use boost to support any and string_view
message(STATUS "use c++14")
find_package(Boost 1.61.0 REQUIRED)
target_include_directories(simple_reverse_proxy PRIVATE ${Boost_INCLUDE_DIRS})
else()
message(STATUS "use c++17")
endif()
aux_source_directory(controllers CTL_SRC)
aux_source_directory(filters FILTER_SRC)
aux_source_directory(plugins PLUGIN_SRC)
aux_source_directory(models MODEL_SRC)
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/views/*.csp)
foreach(cspFile ${SCP_LIST})
message(STATUS "cspFile:" ${cspFile})
EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
message(STATUS "view classname:" ${classname})
ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
COMMAND drogon_ctl
ARGS create view ${cspFile}
DEPENDS ${cspFile}
VERBATIM )
set(VIEWSRC ${VIEWSRC} ${classname}.cc)
endforeach()
target_include_directories(simple_reverse_proxy PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_sources(simple_reverse_proxy PRIVATE ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC})

View File

@ -0,0 +1,3 @@
### An example that shows how to use drogon as an http reverse proxy with a simple round robin.
This project is created with the drogon_ctl command, please compile it after installing drogon.

160
vendors/simple-reverse-proxy/config.json vendored Normal file
View File

@ -0,0 +1,160 @@
/* This is a JSON format configuration file
*/
{
/*
//ssl: The global ssl files setting
"ssl": {
"cert": "../../trantor/trantor/tests/server.crt",
"key": "../../trantor/trantor/tests/server.key"
},*/
"listeners": [{
//address: Ip address,0.0.0.0 by default
"address": "0.0.0.0",
//port: Port number
"port": 8088,
//https: If true, use https for security, false by default
"https": false
}],
"app": {
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
//is the number of CPU cores
"threads_num": 16,
//enable_session: False by default
"enable_session": false,
"session_timeout": 0,
//session_cookie_key: The cookie key of the session, "JSESSIONID" by default
"session_cookie_key": "JSESSIONID",
//session_max_age: The max age of the session cookie, -1 by default
"session_max_age": -1,
//document_root: Root path of HTTP document, default path is ./
"document_root": "./",
//home_page: Set the HTML file of the home page, the default value is "index.html"
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
//to the request for "/".
"home_page": "index.html",
//static_file_headers: Headers for static files
/*"static_file_headers": [
{
"name": "field-name",
"value": "field-value"
}
],*/
//upload_path: The path to save the uploaded file. "uploads" by default.
//If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"upload_path": "uploads",
/* file_types:
* HTTP download file types, the file types supported by drogon
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
* "gif", "bmp", "ico", "icns", etc. */
"file_types": [
"gif",
"png",
"jpg",
"js",
"css",
"html",
"ico",
"swf",
"xap",
"apk",
"cur",
"xml"
],
//max_connections: maximum connections number, 100000 by default
"max_connections": 100000,
//max_connections_per_ip: maximum connections number per client,0 by default which means no limit
"max_connections_per_ip": 0,
//Load_dynamic_views: False by default, when set to true, drogon
//compiles and loads dynamically "CSP View Files" in directories defined
//by "dynamic_views_path"
"load_dynamic_views": false,
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
//it is relative path of document_root path
"dynamic_views_path": [
"./views"
],
//log: Set log output, drogon output logs to stdout by default
"log": {
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
//"log_path": "./",
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
//drogon.log ...
"logfile_base_name": "",
//log_size_limit: 100000000 bytes by default,
//When the log file size reaches "log_size_limit", the log file is switched.
"log_size_limit": 100000000,
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
//The TRACE level is only valid when built in DEBUG mode.
"log_level": "DEBUG"
},
//run_as_daemon: False by default
"run_as_daemon": false,
//handle_sig_term: True by default
"handle_sig_term": true,
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
"relaunch_on_error": false,
//use_sendfile: True by default, if true, the program
//uses sendfile() system-call to send static files to clients;
"use_sendfile": true,
//use_gzip: True by default, use gzip to compress the response body's content;
"use_gzip": true,
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
//0 means cache forever, the negative value means no cache
"static_files_cache_time": 5,
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout": 60,
//server_header_field: Set the 'Server' header field in each response sent by drogon,
//empty string by default with which the 'Server' header field is set to "Server: drogon/version string\r\n"
"server_header_field": "",
//enable_server_header: Set true to force drogon to add a 'Server' header to each HTTP response. The default
//value is true.
"enable_server_header": false,
//enable_date_header: Set true to force drogon to add a 'Date' header to each HTTP response. The default
//value is true.
"enable_date_header": false,
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"keepalive_requests": 0,
//pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
//After the maximum number of requests are made, the connection is closed.
//The default value of 0 means no limit.
"pipelining_requests": 0,
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
//file with the extension ".gz" in the same path and send the compressed file to the client.
//The default value of gzip_static is true.
"gzip_static": true,
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_body_size": "1M",
//max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
//If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
//Setting it to "" means no limit.
"client_max_memory_body_size": "64K",
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
"client_max_websocket_message_size": "128K"
},
//plugins: Define all plugins running in the application
"plugins": [{
//name: The class name of the plugin
"name": "my_plugin::SimpleReverseProxy",
//dependencies: Plugins that the plugin depends on. It can be commented out
"dependencies": [],
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
//It can be commented out
"config": {
// The pipelining depth of HTTP clients.
"pipelining": 32,
"backends": ["http://127.0.0.1:8848"],
"same_client_to_same_backend": false,
//The number of connections created by proxy for each backend in very event loop (IO thread).
"connection_factor": 1
}
}],
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
"custom_config": {}
}

10
vendors/simple-reverse-proxy/main.cc vendored Normal file
View File

@ -0,0 +1,10 @@
#include <drogon/drogon.h>
int main()
{
// Set HTTP listener address and port
drogon::app().loadConfigFile("config.json");
// Run HTTP framework, the method will block in the internal event loop
drogon::app().run();
return 0;
}

View File

@ -0,0 +1,100 @@
/**
*
* @file SimpleReverseProxy.cc
*
*/
#include "SimpleReverseProxy.h"
using namespace drogon;
using namespace my_plugin;
void SimpleReverseProxy::initAndStart(const Json::Value &config)
{
/// Initialize and start the plugin
if (config.isMember("backends") && config["backends"].isArray())
{
for (auto &backend : config["backends"])
{
backendAddrs_.emplace_back(backend.asString());
}
if (backendAddrs_.empty())
{
LOG_ERROR << "You must set at least one backend";
abort();
}
}
else
{
LOG_ERROR << "Error in configuration";
abort();
}
pipeliningDepth_ = config.get("pipelining", 0).asInt();
sameClientToSameBackend_ =
config.get("same_client_to_same_backend", false).asBool();
connectionFactor_ = config.get("connection_factor", 1).asInt();
if (connectionFactor_ == 0 || connectionFactor_ > 100)
{
LOG_ERROR << "invalid number of connection factor";
abort();
}
clients_.init(
[this](std::vector<HttpClientPtr> &clients, size_t ioLoopIndex) {
clients.resize(backendAddrs_.size() * connectionFactor_);
});
clientIndex_.init(
[this](size_t &index, size_t ioLoopIndex) { index = ioLoopIndex; });
drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req,
AdviceCallback &&callback,
AdviceChainCallback &&pass) {
preRouting(req, std::move(callback), std::move(pass));
});
}
void SimpleReverseProxy::shutdown()
{
}
void SimpleReverseProxy::preRouting(const HttpRequestPtr &req,
AdviceCallback &&callback,
AdviceChainCallback &&)
{
size_t index;
auto &clientsVector = *clients_;
if (sameClientToSameBackend_)
{
index = std::hash<uint32_t>{}(req->getPeerAddr().ipNetEndian()) %
clientsVector.size();
index = (index + (++(*clientIndex_)) * backendAddrs_.size()) %
clientsVector.size();
}
else
{
index = ++(*clientIndex_) % clientsVector.size();
}
auto &clientPtr = clientsVector[index];
if (!clientPtr)
{
auto &addr = backendAddrs_[index % backendAddrs_.size()];
clientPtr = HttpClient::newHttpClient(
addr, trantor::EventLoop::getEventLoopOfCurrentThread());
clientPtr->setPipeliningDepth(pipeliningDepth_);
}
req->setPassThrough(true);
clientPtr->sendRequest(
req,
[callback = std::move(callback)](ReqResult result,
const HttpResponsePtr &resp) {
if (result == ReqResult::Ok)
{
resp->setPassThrough(true);
callback(resp);
}
else
{
auto errResp = HttpResponse::newHttpResponse();
errResp->setStatusCode(k500InternalServerError);
callback(errResp);
}
});
}

View File

@ -0,0 +1,44 @@
/**
*
* @file SimpleReverseProxy.h
*
*/
#pragma once
#include <drogon/plugins/Plugin.h>
#include <drogon/drogon.h>
#include <vector>
#include <memory>
namespace my_plugin
{
class SimpleReverseProxy : public drogon::Plugin<SimpleReverseProxy>
{
public:
SimpleReverseProxy()
{
}
/// This method must be called by drogon to initialize and start the plugin.
/// It must be implemented by the user.
void initAndStart(const Json::Value &config) override;
/// This method must be called by drogon to shutdown the plugin.
/// It must be implemented by the user.
void shutdown() override;
private:
// Create 'connectionFactor_' HTTP clients for every backend in every IO
// event loop.
drogon::IOThreadStorage<std::vector<drogon::HttpClientPtr>> clients_;
drogon::IOThreadStorage<size_t> clientIndex_{0};
std::vector<std::string> backendAddrs_;
bool sameClientToSameBackend_{false};
size_t pipeliningDepth_{0};
void preRouting(const drogon::HttpRequestPtr &,
drogon::AdviceCallback &&,
drogon::AdviceChainCallback &&);
size_t connectionFactor_{1};
};
} // namespace my_plugin