/* * 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_