Fork FCGI process
FossilOrigin-Name: 89820a01105e1a7ee10efbb5e072c1d0807a1cd730eaf00f6ed6b0a290b39c69
This commit is contained in:
parent
96a8adbb20
commit
ecc0d91cb5
5 changed files with 121 additions and 28 deletions
|
@ -18,7 +18,11 @@ database:
|
|||
db_file: "wormhole.sqlite"
|
||||
|
||||
# frontend:
|
||||
# # If you specify multiple frontends configs and want to easily switch,
|
||||
# # fe_type just ensures which one to use (but if you use just one, it's not required)
|
||||
# fe_type: "fcgi"
|
||||
|
||||
#
|
||||
# fcgi:
|
||||
# exec: "/usr/bin/treebird"
|
||||
# # Note: If you aren't building on GLIBC, this must be the
|
||||
# # full path to the executable.
|
||||
# exec: "treebird"
|
||||
|
|
|
@ -17,9 +17,14 @@
|
|||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include "fcgi.h"
|
||||
#include "config/config_loader.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr int FCGI_LISTENSOCK_FILENO = STDIN_FILENO;
|
||||
|
||||
HTTP::Response Route::fcgi_request(std::any& args, const HTTP::Request& req, const HTTP::RequestArgs_t& arg)
|
||||
{
|
||||
|
@ -27,16 +32,71 @@ HTTP::Response Route::fcgi_request(std::any& args, const HTTP::Request& req, con
|
|||
return HTTP::Response{ "Hello FCGI"s + req.get_url(), HTTP::MIME::HTML };
|
||||
}
|
||||
|
||||
namespace {
|
||||
void setup_routes(HTTP::Server* server)
|
||||
{
|
||||
// TODO Use bitmasks for this, fucking hell...
|
||||
server->add_route({{HTTP::Request::Type::GET, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::PUT, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::POST, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::DELETE, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::PATCH, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::OTHER, "/:"}, Route::fcgi_request});
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::init_fcgi(HTTP::Server* server)
|
||||
{
|
||||
if (Config::instance().config.frontend.type != Type::FCGI)
|
||||
return;
|
||||
|
||||
// TODO Use bitmasks for this, fucking hell...
|
||||
server->add_route({{HTTP::Request::Type::GET, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::PUT, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::POST, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::DELETE, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::PATCH, "/:"}, Route::fcgi_request});
|
||||
server->add_route({{HTTP::Request::Type::OTHER, "/:"}, Route::fcgi_request});
|
||||
setup_routes(server);
|
||||
}
|
||||
|
||||
void Frontend::poll_data(std::array<int, 2> data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::array<int, 2> Frontend::fork_fcgi_process()
|
||||
{
|
||||
if (Config::instance().config.frontend.type != Type::FCGI)
|
||||
return {-1, -1};
|
||||
|
||||
std::array<int, 2> fd;
|
||||
const std::string& exec = Config::instance().config.frontend.exec;
|
||||
|
||||
pipe(fd.data());
|
||||
|
||||
int pid = fork();
|
||||
bool is_parent = pid != 0;
|
||||
if (is_parent)
|
||||
{
|
||||
Logger::instance() << "Executing frontend \"" + exec + "\" in process " + std::to_string(pid) + "...";
|
||||
return fd;
|
||||
}
|
||||
|
||||
// We are the child process from here onwards
|
||||
|
||||
// FCGI says these aren't open
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
char* argv[] = {
|
||||
nullptr
|
||||
};
|
||||
|
||||
int rc;
|
||||
rc =
|
||||
#ifdef _GNU_SOURCE
|
||||
// Checks PATH as well
|
||||
execvpe
|
||||
#else
|
||||
execve
|
||||
#endif
|
||||
(exec.data(), argv, nullptr);
|
||||
|
||||
Logger::instance() << "Child process \"" + exec + "\" exited with code " + std::to_string(rc);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include "http/httpserver.h"
|
||||
|
||||
namespace Route
|
||||
|
@ -28,4 +29,7 @@ namespace Route
|
|||
namespace Frontend
|
||||
{
|
||||
void init_fcgi(HTTP::Server* server);
|
||||
void poll_data(std::array<int, 2> data);
|
||||
|
||||
std::array<int, 2> fork_fcgi_process();
|
||||
}
|
||||
|
|
|
@ -18,20 +18,20 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <microhttpd.h>
|
||||
#include "logger.h"
|
||||
#include "microhttpd_server.h"
|
||||
#include "http/mime.h"
|
||||
#include "http/request.h"
|
||||
#include <microhttpd.h>
|
||||
#include "http/response.h"
|
||||
|
||||
constexpr int POSTBUFFERSIZE = 2048;
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace HTTP;
|
||||
|
||||
constexpr int POSTBUFFERSIZE = 2048;
|
||||
|
||||
// Struct we define to hold onto data
|
||||
struct MHDConnectionState
|
||||
{
|
||||
|
@ -173,16 +173,26 @@ MicroHttpdServer::MicroHttpdServer(std::uint16_t port, std::any callback_args)
|
|||
|
||||
void MicroHttpdServer::start()
|
||||
{
|
||||
serv.reset(MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
|
||||
get_port(),
|
||||
nullptr,
|
||||
0,
|
||||
&::new_connection,
|
||||
this,
|
||||
MHD_OPTION_NOTIFY_CONNECTION,
|
||||
request_completed,
|
||||
this,
|
||||
MHD_OPTION_END));
|
||||
MHD_Daemon* dm = MHD_start_daemon(MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
|
||||
get_port(),
|
||||
nullptr,
|
||||
0,
|
||||
&::new_connection,
|
||||
this,
|
||||
MHD_OPTION_NOTIFY_CONNECTION,
|
||||
request_completed,
|
||||
this,
|
||||
MHD_OPTION_END);
|
||||
if (!dm)
|
||||
{
|
||||
// MicroHTTPD seems to keep the errors in errno, but doesn't really have a good error system
|
||||
Logger::instance().log("Couldn't start MicroHTTPD daemon at port "s + std::to_string(get_port()) +
|
||||
": " + strerror(errno),
|
||||
Logger::Level::ERROR);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
serv.reset(dm);
|
||||
}
|
||||
|
||||
|
||||
|
|
23
src/main.cpp
23
src/main.cpp
|
@ -71,6 +71,7 @@ void init_routes(const std::unique_ptr<HTTP::Server>& server)
|
|||
Protocol::MastoAPI::init_masto_api(server.get());
|
||||
#endif // MODULE_MASTO_API
|
||||
|
||||
// Global routes
|
||||
#ifdef MODULE_FCGI
|
||||
Frontend::init_fcgi(server.get());
|
||||
#endif // MODULE_FCGI
|
||||
|
@ -83,6 +84,11 @@ int start_wormhole()
|
|||
Logger::instance() << "Loading config...";
|
||||
|
||||
std::shared_ptr<DB::Database> database = DB::load_db_from_cfg();
|
||||
|
||||
#ifdef MODULE_FCGI
|
||||
// Config loaded, fork main FCGI process early if set in config
|
||||
auto pipe_fds = Frontend::fork_fcgi_process();
|
||||
#endif
|
||||
|
||||
// Passed as std::any to each route function
|
||||
RouteArgs args{database.get()};
|
||||
|
@ -133,12 +139,21 @@ int start_wormhole()
|
|||
} },
|
||||
});
|
||||
|
||||
// Start the server!
|
||||
// Start the server! This forks in the background
|
||||
server->start();
|
||||
|
||||
Logger::instance() << "HTTP server running on port "s + std::to_string(Config::instance().config.http.port);
|
||||
|
||||
getchar();
|
||||
Logger::instance() << "HTTP server running on port "s +
|
||||
std::to_string(Config::instance().config.http.port);
|
||||
|
||||
/* So... now what?
|
||||
* Well, if we are building with FCGI, we can use this thread
|
||||
* to listen to our pipes */
|
||||
#ifdef MODULE_FCGI
|
||||
Frontend::poll_data(pipe_fds);
|
||||
#endif
|
||||
Logger::instance() << "^D or enter 'q' to stop";
|
||||
char c;
|
||||
while ((c = getchar()) != EOF && c != 'q' && c != 'Q');
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue