Added my web app implementation to the drogon module. Just renamed it and made it compile.

This commit is contained in:
Relintai 2021-06-20 17:53:07 +02:00
parent 9e0621daf0
commit c4c9d34e35
7 changed files with 619 additions and 0 deletions

View File

@ -5,6 +5,7 @@ Import("env")
env_mod.core_sources = []
env_mod.add_source_files(env_mod.core_sources, "*.cpp")
env_mod.add_source_files(env_mod.core_sources, "drogon/lib/src/*.cc")
env_mod.add_source_files(env_mod.core_sources, "drogon/lib/inc/http/*.cc")
env_mod.add_source_files(env_mod.core_sources, "drogon/lib/src/ssl_funcs/*.cc")

View File

@ -0,0 +1,13 @@
#include "handler_instance.h"
#include "request.h"
#include "core/object.h"
DHandlerInstance::DHandlerInstance() {
instance = nullptr;
}
DHandlerInstance::DHandlerInstance(std::function<void(Object *, DRequest *)> p_handler_func, Object *p_instance) {
handler_func = p_handler_func;
instance = p_instance;
}

View File

@ -0,0 +1,17 @@
#ifndef DHANDLER_INSTANCE_H
#define DHANDLER_INSTANCE_H
#include <functional>
class Object;
class DRequest;
struct DHandlerInstance {
std::function<void(Object *, DRequest *)> handler_func;
Object *instance;
DHandlerInstance();
DHandlerInstance(std::function<void(Object *, DRequest *)> p_handler_func, Object *p_instance = nullptr);
};
#endif

309
modules/drogon/request.cpp Normal file
View File

@ -0,0 +1,309 @@
#include "request.h"
#include "web_application.h"
void DRequest::compile_body() {
compiled_body.reserve(body.size() + head.size() + 13 + 14 + 15);
//13
compiled_body += "<html>"
"<head>";
compiled_body += head;
//14
compiled_body += "</head>"
"<body>";
compiled_body += body;
compiled_body += footer;
//15
compiled_body += "</body>"
"</html>";
response->setBody(compiled_body);
}
void DRequest::compile_and_send_body() {
compile_body();
send();
}
void DRequest::next_stage() {
if (current_middleware_index == (*middleware_stack).size()) {
handler_instance.handler_func(handler_instance.instance, this);
return;
}
const DHandlerInstance &hi = (*middleware_stack)[current_middleware_index++];
hi.handler_func(hi.instance, this);
}
void DRequest::send() {
//if (connection_closed) {
// DRequestPool::return_request(this);
// return;
//}
if (http_parser->isKeepAlive()) {
response->addHeadValue("Connection", "Keep-Alive");
std::string result = response->getResult();
session->send(result.c_str(), result.size());
} else {
response->addHeadValue("Connection", "Close");
std::string result = response->getResult();
HttpSession::Ptr lsession = session;
session->send(result.c_str(), result.size(), [lsession]() { lsession->postShutdown(); });
}
DRequestPool::return_request(this);
}
void DRequest::send_file(const std::string &p_file_path) {
//if (connection_closed) {
// DRequestPool::return_request(this);
// return;
//}
file_path = p_file_path;
FILE *f = fopen(file_path.c_str(), "rb");
if (!f) {
printf("send_file: Error: Download: file doesn't exists! %s\n", file_path.c_str());
return;
}
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fclose(f);
response->addHeadValue("Connection", "Close");
std::string result = "HTTP/1.1 200 OK\r\nConnection: Close\r\n\r\n";
application->register_request_update(this);
session->send(result.c_str(), result.size(), [this]() { this->_file_chunk_sent(); });
}
void DRequest::send_error(int error_code) {
application->send_error(error_code, this);
}
void DRequest::reset() {
application = nullptr;
http_parser = nullptr;
session = nullptr;
current_middleware_index = 0;
middleware_stack = nullptr;
_path_stack.clear();
_path_stack_pointer = 0;
file_size = 0;
current_file_progress = 0;
connection_closed = false;
head.clear();
body.clear();
footer.clear();
compiled_body.clear();
if (response)
delete response;
response = new HttpResponse();
}
void DRequest::setup_url_stack() {
std::string path = http_parser->getPath();
size_t pos = 0;
std::string st;
while ((pos = path.find("/")) != std::string::npos) {
st = path.substr(0, pos);
if (st.size() != 0)
_path_stack.push_back(st);
path.erase(0, pos + 1);
}
if (path.size() != 0)
_path_stack.push_back(path);
}
std::string DRequest::get_path() const {
std::string path = "";
for (uint32_t i = _path_stack_pointer; i < _path_stack.size(); ++i) {
path += _path_stack[i];
path += "/";
}
return path;
}
const std::string &DRequest::get_path_full() const {
return http_parser->getPath();
}
const std::string &DRequest::get_path_segment(const uint32_t i) const {
return _path_stack[i];
}
const std::string &DRequest::get_current_path_segment() const {
if (_path_stack_pointer >= _path_stack.size()) {
//for convenience
static const std::string e_str = "";
return e_str;
}
return _path_stack[_path_stack_pointer];
}
uint32_t DRequest::get_path_segment_count() const {
return _path_stack.size();
}
uint32_t DRequest::get_current_segment_index() const {
return _path_stack_pointer;
}
uint32_t DRequest::get_remaining_segment_count() const {
if (_path_stack_pointer > _path_stack.size()) {
return 0;
}
return _path_stack.size() - _path_stack_pointer;
}
void DRequest::pop_path() {
_path_stack_pointer -= 1;
}
void DRequest::push_path() {
_path_stack_pointer += 1;
}
void DRequest::update() {
if (file_next) {
file_next = false;
_progress_send_file();
}
}
DRequest::DRequest() {
response = nullptr;
//This value will need benchmarks, 2 MB seems to be just as fast for me as 4 MB, but 1MB is slower
//It is a tradeoff on server memory though, as every active download will consume this amount of memory
//where the file is bigger than this number
file_chunk_size = 1 << 21; //2MB
reset();
}
DRequest::~DRequest() {
delete response;
}
void DRequest::_progress_send_file() {
if (connection_closed) {
DRequestPool::return_request(this);
return;
}
if (current_file_progress >= file_size) {
session->postShutdown();
DRequestPool::return_request(this);
return;
}
FILE *f = fopen(file_path.c_str(), "rb");
if (!f) {
printf("Error: Download: In progress file doesn't exists anymore! %s\n", file_path.c_str());
application->unregister_request_update(this);
session->postShutdown();
DRequestPool::return_request(this);
return;
}
fseek(f, current_file_progress, SEEK_SET);
long nfp = current_file_progress + file_chunk_size;
long csize = file_chunk_size;
if (nfp >= file_size) {
csize = (file_size - current_file_progress);
}
body.resize(csize);
fread(&body[0], 1, csize, f);
fclose(f);
current_file_progress = nfp;
session->send(body.c_str(), body.size(), [this]() { this->_file_chunk_sent(); });
}
void DRequest::_file_chunk_sent() {
file_next = true;
}
DRequest *DRequestPool::get_request() {
_mutex.lock();
DRequest *request;
if (_requests.size() == 0) {
_mutex.unlock();
request = new DRequest();
return request;
}
request = _requests[_requests.size() - 1];
_requests.pop_back();
_mutex.unlock();
request->reset();
return request;
}
void DRequestPool::return_request(DRequest *request) {
_mutex.lock();
_requests.push_back(request);
_mutex.unlock();
}
DRequestPool::DRequestPool() {
}
DRequestPool::~DRequestPool() {
for (uint32_t i = 0; i < _requests.size(); ++i) {
delete _requests[i];
}
_requests.clear();
}
std::mutex DRequestPool::_mutex;
std::vector<DRequest *> DRequestPool::_requests;

83
modules/drogon/request.h Normal file
View File

@ -0,0 +1,83 @@
#ifndef DREQUEST_H
#define REQUEST_H
#include <mutex>
#include <vector>
#include <brynet/net/http/HttpFormat.hpp>
#include <brynet/net/http/HttpService.hpp>
#include "handler_instance.h"
class DWebApplication;
class DRequest {
public:
HTTPParser::Ptr http_parser;
HttpSession::Ptr session;
HttpResponse *response;
DWebApplication *application;
uint32_t current_middleware_index;
DHandlerInstance handler_instance;
std::vector<DHandlerInstance> *middleware_stack;
std::string head;
std::string body;
std::string footer;
std::string compiled_body;
std::string file_path;
long file_size;
long current_file_progress;
long file_chunk_size;
bool file_next;
bool connection_closed;
void compile_body();
void compile_and_send_body();
void next_stage();
void send();
void send_file(const std::string &p_file_path);
void send_error(int error_code);
void reset();
void setup_url_stack();
std::string get_path() const;
const std::string &get_path_full() const;
const std::string &get_path_segment(const uint32_t i) const;
const std::string &get_current_path_segment() const;
uint32_t get_path_segment_count() const;
uint32_t get_current_segment_index() const;
uint32_t get_remaining_segment_count() const;
void pop_path();
void push_path();
void update();
DRequest();
~DRequest();
protected:
void _progress_send_file();
void _file_chunk_sent();
std::vector<std::string> _path_stack;
uint32_t _path_stack_pointer;
};
class DRequestPool {
public:
static DRequest *get_request();
static void return_request(DRequest *request);
DRequestPool();
~DRequestPool();
protected:
static std::mutex _mutex;
static std::vector<DRequest *> _requests;
};
#endif

View File

@ -0,0 +1,140 @@
#include "web_application.h"
#include <functional>
#include <string>
#include "request.h"
#include "core/file_cache.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
void DWebApplication::load_settings() {
}
void DWebApplication::setup_routes() {
default_error_handler_func = DWebApplication::default_fallback_error_handler;
error_handler_map[404] = DWebApplication::default_404_error_handler;
}
void DWebApplication::setup_middleware() {
middlewares.push_back(DHandlerInstance([this](Object *instance, DRequest *request){ this->default_routing_middleware(instance, request); }));
}
void DWebApplication::default_routing_middleware(Object *instance, DRequest *request) {
std::string path = request->http_parser->getPath();
if (FileCache::get_singleton()->wwwroot_has_file(path)) {
send_file(path, request);
return;
}
DHandlerInstance handler_data;
//std::function<void(Object *, Request *)> func;
//if (path == "/") {
if (request->get_path_segment_count() == 0) {
//quick shortcut
handler_data = index_func;
} else {
const std::string main_route = request->get_current_path_segment();
handler_data = main_route_map[main_route];
request->push_path();
}
if (!handler_data.handler_func) {
send_error(404, request);
return;
}
request->handler_instance = handler_data;
request->next_stage();
}
void DWebApplication::default_fallback_error_handler(int error_code, DRequest *request) {
request->response->setBody(default_generic_error_body);
request->send();
}
void DWebApplication::default_404_error_handler(int error_code, DRequest *request) {
request->response->setBody(default_error_404_body);
request->send();
}
void DWebApplication::handle_request(DRequest *request) {
request->middleware_stack = &middlewares;
//note that middlewares handle the routing -> DWebApplication::default_routing_middleware by default
request->next_stage();
}
void DWebApplication::send_error(int error_code, DRequest *request) {
std::function<void(int, DRequest *)> func = error_handler_map[error_code];
if (!func) {
default_error_handler_func(error_code, request);
return;
}
func(error_code, request);
}
void DWebApplication::send_file(const std::string &path, DRequest *request) {
std::string fp = FileCache::get_singleton()->wwwroot + path;
request->send_file(fp);
}
void DWebApplication::migrate() {
}
void DWebApplication::register_request_update(DRequest *request) {
std::lock_guard<std::mutex> lock(_update_registered_requests_mutex);
_update_registered_requests.push_back(request);
}
void DWebApplication::unregister_request_update(DRequest *request) {
std::lock_guard<std::mutex> lock(_update_registered_requests_mutex);
std::size_t s = _update_registered_requests.size();
for (std::size_t i = 0; i < s; ++i) {
DRequest *r = _update_registered_requests[i];
if (r == request) {
_update_registered_requests[i] = _update_registered_requests[s - 1];
_update_registered_requests.pop_back();
return;
}
}
}
void DWebApplication::update() {
for (std::size_t i = 0; i < _update_registered_requests.size(); ++i) {
DRequest *r = _update_registered_requests[i];
r->update();
}
}
DWebApplication::DWebApplication() {
}
DWebApplication::~DWebApplication() {
main_route_map.clear();
error_handler_map.clear();
middlewares.clear();
}
std::string DWebApplication::default_error_404_body = "<html><body>404 :(</body></html>";
std::string DWebApplication::default_generic_error_body = "<html><body>Internal server error! :(</body></html>";

View File

@ -0,0 +1,56 @@
#ifndef DWEB_APPLICATION_H
#define WEB_APPLICATION_H
#include "core/object.h"
#include <functional>
#include <map>
#include <string>
#include <vector>
#include <mutex>
#include "handler_instance.h"
class DRequest;
class DWebApplication {
public:
static std::string default_error_404_body;
static std::string default_generic_error_body;
void handle_request(DRequest *request);
void send_error(int error_code, DRequest *request);
void send_file(const std::string &path, DRequest *request);
static void default_fallback_error_handler(int error_code, DRequest *request);
static void default_404_error_handler(int error_code, DRequest *request);
virtual void load_settings();
virtual void setup_routes();
virtual void setup_middleware();
void default_routing_middleware(Object *instance, DRequest *request);
virtual void migrate();
void register_request_update(DRequest *request);
void unregister_request_update(DRequest *request);
void update();
DWebApplication();
virtual ~DWebApplication();
public:
DHandlerInstance index_func;
std::map<std::string, DHandlerInstance> main_route_map;
std::vector<DHandlerInstance> middlewares;
std::map<int, std::function<void(int, DRequest *)> > error_handler_map;
std::function<void(int, DRequest *)> default_error_handler_func;
protected:
std::mutex _update_registered_requests_mutex;
std::vector<DRequest *> _update_registered_requests;
};
#endif