// Main unit test compilation unit // boost mandates that exactly one compilation unit contains the following two lines: #define BOOST_TEST_MODULE webserver_test #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "webserver.h" using namespace std::string_literals; namespace fs = std::filesystem; namespace pt = boost::property_tree; using namespace Reichwein; class WebserverProcess { public: const fs::path testConfigFilename{"./webserver.conf"}; WebserverProcess(): m_pid{} { File::setFile(testConfigFilename, R"CONFIG( www-data www-data 10 ../plugins localhost ip6-localhost localhost 127.0.0.1 [::1] static-files .
127.0.0.1
8080 http localhost
::1
8080 http localhost
)CONFIG"); start(); } ~WebserverProcess() { stop(); fs::remove(testConfigFilename); } void start() { if (m_pid != 0) throw std::runtime_error("Process already running, so it can't be started"); m_pid = fork(); if (m_pid < 0) throw std::runtime_error("Fork unsuccessful."); if (m_pid == 0) { // child process branch char* argv[] = {(char*)"webserver", (char*)"-c", (char*)"./webserver.conf"}; webserver(sizeof(argv) / sizeof(char*), argv); exit(0); } // wait for server to start up std::this_thread::sleep_for(std::chrono::milliseconds(100)); } void stop() { if (m_pid == 0) throw std::runtime_error("Process not running, so it can't be stopped"); if (kill(m_pid, SIGTERM) != 0) throw std::runtime_error("Unable to kill process"); if (int result = waitpid(m_pid, NULL, 0); result != m_pid) throw std::runtime_error("waitpid returned "s + std::to_string(result)); m_pid = 0; } bool isRunning() { if (m_pid == 0) return false; fs::path pid_file{fmt::format("/proc/{}/stat", m_pid)}; if (!fs::exists(pid_file)) return false; std::string s{File::getFile(pid_file)}; auto pos0{s.find(' ', 0)}; pos0 = s.find(' ', pos0 + 1); pos0++; auto pos1{s.find(' ', pos0 + 1)}; std::string state{s.substr(pos0, pos1 - pos0)}; return state == "R" || state == "S"; } private: pid_t m_pid; }; std::string HTTPGet(const std::string& target) { auto const host = "127.0.0.1"; //"::1"; auto const port = "8080"; int version = 11; // or 10 // The io_context is required for all I/O boost::asio::io_context ioc; // These objects perform our I/O boost::asio::ip::tcp::resolver resolver(ioc); boost::beast::tcp_stream stream(ioc); // Look up the domain name auto const results = resolver.resolve(host, port); // Make the connection on the IP address we get from a lookup stream.connect(results); // Set up an HTTP GET request message boost::beast::http::request req{boost::beast::http::verb::get, target, version}; req.set(boost::beast::http::field::host, host); req.set(boost::beast::http::field::user_agent, "Webserver Testsuite"); // Send the HTTP request to the remote host boost::beast::http::write(stream, req); // This buffer is used for reading and must be persisted boost::beast::flat_buffer buffer; // Declare a container to hold the response boost::beast::http::response res; // Receive the HTTP response boost::beast::http::read(stream, buffer, res); // Write the message to standard out std::ostringstream header_stream; header_stream << res.base(); //std::ostringstream body_stream; header_stream << boost::beast::buffers_to_string(res.body().data()); // Gracefully close the socket boost::beast::error_code ec; stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); // not_connected happens sometimes // so don't bother reporting it. // if(ec && ec != boost::beast::errc::not_connected) throw boost::beast::system_error{ec}; return header_stream.str(); } class Fixture { public: Fixture(){} ~Fixture(){} }; BOOST_FIXTURE_TEST_CASE(http_download, Fixture) { WebserverProcess serverProcess; std::cout << HTTPGet("/webserver.conf"); }