From d14582a1d92e036780166a0b5ec0494d7353cc75 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Fri, 13 Jan 2023 16:20:42 +0100 Subject: Implemented and tested managed FCGI application start, separated out process check functions to libreichwein --- tests/Makefile | 8 ++- tests/fastcgiprocess.cpp | 115 --------------------------------------- tests/fastcgiprocess.h | 28 ---------- tests/helper.cpp | 62 +-------------------- tests/helper.h | 3 - tests/test-webserver.cpp | 91 ++++++++++++++++++++++++++++++- tests/webserverprocess.cpp | 2 +- tests/websocketserverprocess.cpp | 2 +- 8 files changed, 99 insertions(+), 212 deletions(-) delete mode 100644 tests/fastcgiprocess.cpp delete mode 100644 tests/fastcgiprocess.h (limited to 'tests') diff --git a/tests/Makefile b/tests/Makefile index 8df5a45..898afbf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -17,7 +17,7 @@ CXXFLAGS+= -I. -I.. -fPIE CXXTESTFLAGS= -CXXFLAGS+=$(shell pkg-config --cflags fmt fcgi) +CXXFLAGS+=$(shell pkg-config --cflags fcgi) LIBS+=\ -lreichwein \ @@ -29,7 +29,7 @@ LIBS+=\ -lpthread \ -lssl -lcrypto \ -ldl \ -$(shell pkg-config --libs fmt fcgi) +$(shell pkg-config --libs fcgi) LDFLAGS+=-pie @@ -37,6 +37,7 @@ UNITS=\ auth.cpp \ config.cpp \ error.cpp \ + fastcgiprocess.cpp \ http.cpp \ plugin.cpp \ privileges.cpp \ @@ -57,7 +58,6 @@ TESTSRC=\ test-server.cpp \ test-statistics.cpp \ test-webserver.cpp \ - fastcgiprocess.cpp \ helper.cpp \ webserverprocess.cpp \ websocketserverprocess.cpp @@ -93,6 +93,8 @@ config.o: ../config.cpp $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ error.o: ../error.cpp $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ +fastcgiprocess.o: ../plugins/fcgi/fastcgiprocess.cpp + $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ http.o: ../http.cpp $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ plugin.o: ../plugin.cpp diff --git a/tests/fastcgiprocess.cpp b/tests/fastcgiprocess.cpp deleted file mode 100644 index 53b9d04..0000000 --- a/tests/fastcgiprocess.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "fastcgiprocess.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "helper.h" - -using namespace std::string_literals; -namespace fs = std::filesystem; -namespace pt = boost::property_tree; -using namespace boost::unit_test; -using namespace Reichwein; - -#define FCGI_LISTENSOCK_FILENO 0 - -FastCGIProcess::FastCGIProcess(const std::filesystem::path& path, const std::string& host, unsigned short port): - m_pid{}, - m_command{path.generic_string()}, - m_host{host}, - m_port{port} -{ - start(); -} - -FastCGIProcess::~FastCGIProcess() -{ - stop(); -} - -void FastCGIProcess::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 - try { - boost::asio::io_context ioc; - boost::asio::ip::tcp::resolver resolver(ioc); - auto const results = resolver.resolve(m_host.c_str(), std::to_string(m_port).c_str()); - if (results.begin() == results.end()) - std::runtime_error("no resolve result"); - boost::asio::ip::tcp::endpoint endpoint{*results.begin()}; - boost::asio::ip::tcp::acceptor acceptor(ioc); - acceptor.open(endpoint.protocol()); - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor.bind(endpoint); - acceptor.listen(); - int fd{acceptor.native_handle()}; - - if (fd != FCGI_LISTENSOCK_FILENO) { - close(FCGI_LISTENSOCK_FILENO); - dup2(fd, FCGI_LISTENSOCK_FILENO); - close(fd); - } - - execl(m_command.c_str(), m_command.c_str(), (const char*)nullptr); - } catch (const std::exception& ex) { - std::cout << "FastCGI process error: " << ex.what() << std::endl; - } - exit(0); - } - - // wait for server to start up - wait_for_pid_listening_on(m_pid, m_port); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); -} - -void FastCGIProcess::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 FastCGIProcess::is_running() -{ - if (m_pid == 0) - return false; - - return Reichwein::Process::is_running(m_pid); -} - diff --git a/tests/fastcgiprocess.h b/tests/fastcgiprocess.h deleted file mode 100644 index ce7bf74..0000000 --- a/tests/fastcgiprocess.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -class FastCGIProcess -{ -public: - FastCGIProcess(const std::filesystem::path& path, const std::string& host, unsigned short port); - ~FastCGIProcess(); - bool is_running(); - -private: - void start(); - void stop(); - - pid_t m_pid; - std::string m_command; - std::string m_host; - unsigned short m_port; -}; diff --git a/tests/helper.cpp b/tests/helper.cpp index 644b9ca..66045fb 100644 --- a/tests/helper.cpp +++ b/tests/helper.cpp @@ -1,5 +1,7 @@ #include "helper.h" +#include + using namespace std::string_literals; namespace fs = std::filesystem; namespace pt = boost::property_tree; @@ -10,66 +12,6 @@ const fs::path testConfigFilename{"./webserver.conf"}; const fs::path testCertFilename{"./testchain.pem"}; const fs::path testKeyFilename{"./testkey.pem"}; -// tcp: tcp or tcp6 -bool tcp_is_pid_listening_on(const std::string& tcp, pid_t pid, int port) -{ - std::string filename{fmt::format("/proc/{}/net/{}", pid, tcp)}; - std::ifstream f{filename, std::ios::in}; - // e.g.: - // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode - // 0: 00000000:C799 00000000:0000 0A 00000000:00000000 00:00000000 00000000 107 0 21869 1 00000000335416a4 100 0 0 10 0 - std::string s; - std::getline(f, s); // skip head line - while (std::getline(f, s)) { - boost::algorithm::trim_left(s); - - size_t pos_space1{s.find(' ')}; - if (pos_space1 == std::string::npos) - throw std::runtime_error("Expected first space in " + filename); - - size_t pos_colon1{s.find(':', pos_space1 + 1)}; - if (pos_colon1 == std::string::npos) - throw std::runtime_error("Expected first colon in " + filename); - - size_t pos_space2{s.find(' ', pos_colon1 + 1)}; - if (pos_space2 == std::string::npos) - throw std::runtime_error("Expected second space in " + filename); - - std::string port_s{s.substr(pos_colon1 + 1, pos_space2 - (pos_colon1 + 1))}; - auto current_port{std::stoul(port_s, nullptr, 16)}; - if (current_port != port) - continue; - - // now, we are in a line related to matching local port - - size_t pos_space3{s.find(' ', pos_space2 + 1)}; - if (pos_space3 == std::string::npos) - throw std::runtime_error("Expected third space in " + filename); - - size_t pos_space4{s.find(' ', pos_space3 + 1)}; - if (pos_space4 == std::string::npos) - throw std::runtime_error("Expected fourth space in " + filename); - - std::string state_s{s.substr(pos_space3 + 1, pos_space4 - (pos_space3 + 1))}; - if (state_s == "0A") // listening state TCP_LISTEN, from net/tcp_states.h - return true; - } - - return false; // not found -} - -bool is_pid_listening_on(pid_t pid, int port) -{ - return tcp_is_pid_listening_on("tcp", pid, port) || tcp_is_pid_listening_on("tcp6", pid, port); -} - -void wait_for_pid_listening_on(pid_t pid, int port) -{ - while (!is_pid_listening_on(pid, port)) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } -} - // returns -1 if no port found in config int port_from_config(const std::string& config) { diff --git a/tests/helper.h b/tests/helper.h index eba74cd..be443c6 100644 --- a/tests/helper.h +++ b/tests/helper.h @@ -47,9 +47,6 @@ extern const std::filesystem::path testConfigFilename; extern const std::filesystem::path testCertFilename; extern const std::filesystem::path testKeyFilename; -bool tcp_is_pid_listening_on(const std::string& tcp, pid_t pid, int port); -bool is_pid_listening_on(pid_t pid, int port); -void wait_for_pid_listening_on(pid_t pid, int port); int port_from_config(const std::string& config); void load_root_certificates(boost::asio::ssl::context& ctx); std::pair HTTP(const std::string& target, bool ipv6 = true, bool HTTP11 = true, boost::beast::http::verb method = boost::beast::http::verb::get); diff --git a/tests/test-webserver.cpp b/tests/test-webserver.cpp index bd89aab..5353c6a 100644 --- a/tests/test-webserver.cpp +++ b/tests/test-webserver.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,8 @@ #include "webserver.h" #include "response.h" -#include "fastcgiprocess.h" +#include "plugins/fcgi/fastcgiprocess.h" + #include "helper.h" #include "webserverprocess.h" #include "websocketserverprocess.h" @@ -694,3 +696,90 @@ BOOST_FIXTURE_TEST_CASE(http_fcgi_ip6, Fixture) BOOST_CHECK_EQUAL(result.second, "returning data of : "); } +BOOST_FIXTURE_TEST_CASE(http_fcgi_unix_socket, Fixture) +{ + std::string webserver_config{R"CONFIG( + www-data + www-data + 10 + stats.db + ../plugins + + + localhost + localhost + [::1] + + fcgi + fcgi-socket + + + + + +
::1
+ 8080 + http + localhost +
+
+
)CONFIG"}; + WebserverProcess serverProcess{webserver_config}; + BOOST_REQUIRE(serverProcess.is_running()); + + FastCGIProcess fcgiProcess("./fcgi1", "fcgi-socket"); + BOOST_REQUIRE(fcgiProcess.is_running()); + + auto result {HTTP("/fcgi/abc")}; + BOOST_CHECK_EQUAL(result.first, fmt::format( +"HTTP/1.1 200 OK\r\n" +"Server: Reichwein.IT Webserver {}\r\n" +"Content-Type: text/plain\r\n" +"Content-Length: {}\r\n" +"\r\n" + , VERSION, result.second.size())); + BOOST_CHECK_EQUAL(result.second, "returning data of : "); +} + +BOOST_FIXTURE_TEST_CASE(http_fcgi_executable, Fixture) +{ + std::string webserver_config{R"CONFIG( + www-data + www-data + 10 + stats.db + ../plugins + + + localhost + localhost + [::1] + + fcgi + fcgi1 + + + + + +
::1
+ 8080 + http + localhost +
+
+
)CONFIG"}; + WebserverProcess serverProcess{webserver_config}; + BOOST_REQUIRE(serverProcess.is_running()); + + auto result {HTTP("/fcgi/abc")}; + BOOST_CHECK_EQUAL(result.first, fmt::format( +"HTTP/1.1 200 OK\r\n" +"Server: Reichwein.IT Webserver {}\r\n" +"Content-Type: text/plain\r\n" +"Content-Length: {}\r\n" +"\r\n" + , VERSION, result.second.size())); + BOOST_CHECK_EQUAL(result.second, "returning data of : "); +} + diff --git a/tests/webserverprocess.cpp b/tests/webserverprocess.cpp index c91275b..edafaaa 100644 --- a/tests/webserverprocess.cpp +++ b/tests/webserverprocess.cpp @@ -193,7 +193,7 @@ void WebserverProcess::start() // wait for server to start up if (int port{port_from_config(m_config)}; port >= 0) - wait_for_pid_listening_on(m_pid, port); + Process::wait_for_pid_listening_on(m_pid, port); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } diff --git a/tests/websocketserverprocess.cpp b/tests/websocketserverprocess.cpp index 89a50ee..a0ec13d 100644 --- a/tests/websocketserverprocess.cpp +++ b/tests/websocketserverprocess.cpp @@ -174,7 +174,7 @@ void WebsocketServerProcess::start() exit(0); } - wait_for_pid_listening_on(m_pid, 8765); + Process::wait_for_pid_listening_on(m_pid, 8765); } void WebsocketServerProcess::stop() -- cgit v1.2.3