/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3,
* as published by the Free Software Foundation.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Authored by: Thomas Voß
*/
#ifndef TESTING_WEB_SERVER_H_
#define TESTING_WEB_SERVER_H_
#include
#include
#include
#include
#include
#include
#include
#include "mongoose.h"
namespace testing
{
namespace web
{
namespace server
{
// Configuration options for creating a testing web server.
struct Configuration
{
// The port to expose the web-server on.
std::uint16_t port;
// Function that is invoked for individual client requests.
std::function request_handler;
};
}
}
// Returns an executable web-server for the given configuration.
inline std::function a_web_server(const web::server::Configuration& configuration)
{
return [configuration](core::testing::CrossProcessSync& cps)
{
bool terminated = false;
// Register for SIG_TERM
auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
// On SIG_TERM, we set terminated to false and request a clean shutdown
// of the polling loop.
trap->signal_raised().connect([trap, &terminated](core::posix::Signal)
{
trap->stop();
terminated = true;
});
struct Context
{
static int on_request(mg_connection* conn, mg_event ev)
{
auto thiz = static_cast(conn->server_param);
switch (ev)
{
case MG_REQUEST:
return thiz->handle_request(conn);
case MG_AUTH:
return MG_TRUE;
default:
return MG_FALSE;
}
return MG_FALSE;
}
int handle_request(mg_connection* conn)
{
return configuration.request_handler(conn);
}
const testing::web::server::Configuration& configuration;
} context{configuration};
std::thread trap_worker
{
[trap]()
{
trap->run();
}
};
auto server = mg_create_server(&context, Context::on_request);
// Setup the port on which the server should be exposed.
mg_set_option(server, "listening_port", std::to_string(configuration.port).c_str());
// Notify framework that we are good to go
cps.try_signal_ready_for(std::chrono::milliseconds{500});
// Start the polling loop
for (;;)
{
mg_poll_server(server, 200);
if (terminated)
break;
}
// Cleanup, and free server instance
mg_destroy_server(&server);
if (trap_worker.joinable())
trap_worker.join();
return ::testing::Test::HasFailure() ? core::posix::exit::Status::failure : core::posix::exit::Status::success;
};
}
}
#endif // TESTING_WEB_SERVER_H_