From 1cc484b25547e349177cf652f62021b802f48655 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 3 May 2020 22:52:45 +0200 Subject: Fixed TCP keepalive (workaround) --- debian/control | 1 + plugins/fcgi/fcgi.cpp | 105 ++++++++++++++++++++++++++------------ plugins/fcgi/fcgi.h | 2 +- plugins/statistics/statistics.cpp | 2 +- 4 files changed, 74 insertions(+), 36 deletions(-) diff --git a/debian/control b/debian/control index acd8ae5..50ac6c7 100644 --- a/debian/control +++ b/debian/control @@ -20,6 +20,7 @@ Description: Web server - HTTP and HTTPs via OpenSSL - Virtual Servers - CGI interface + - FastCGI interface - Upload/Download Statistics # weblog: Depends: mpack diff --git a/plugins/fcgi/fcgi.cpp b/plugins/fcgi/fcgi.cpp index 416ca24..464ba75 100644 --- a/plugins/fcgi/fcgi.cpp +++ b/plugins/fcgi/fcgi.cpp @@ -344,59 +344,94 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) std::unordered_map app_values; // will be read by FCGI_GET_VALUES size_t pos { app_addr.find(':') }; - if (pos != app_addr.npos) { // host:port + if (pos != app_addr.npos) { // tcp socket: host:port auto endpoints{m_resolver.resolve(app_addr.substr(0, pos), app_addr.substr(pos + 1))}; bool opening{false}; std::lock_guard socket_lock{m_socket_mutex}; - if (!m_socket.is_open()) { + auto it {m_sockets.find(app_addr)}; + + std::pair::iterator, bool> it2{m_sockets.end(), false}; + if (it == m_sockets.end()) + it2 = m_sockets.emplace(app_addr, m_io_context); // add new element if necessary + + boost::asio::ip::tcp::socket& socket { it2.second ? it2.first->second : it->second }; // use just added element or previously found one + + socket.close(); // TODO: Bug workaround: Keeping TCP socket open doesn't work for now + + if (!socket.is_open()) { std::cout << "FCGI: Opening new socket" << std::endl; - boost::asio::connect(m_socket, endpoints); + + boost::asio::connect(socket, endpoints); + + boost::asio::socket_base::keep_alive keepAlive(true); + socket.set_option(keepAlive); + + struct timeval tv; + tv.tv_sec = 0; // infinite + tv.tv_usec = 0; + if (setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) + std::cerr << "FCGI Error: SO_RCVTIMEO" << std::endl; + + if (setsockopt(socket.native_handle(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv))) + std::cerr << "FCGI Error: SO_SNDTIMEO" << std::endl; + + int val{1}; + if (setsockopt(socket.native_handle(), SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val))) + std::cerr << "FCGI Error: SO_KEEPALIVE" << std::endl; + opening = true; } - if (!m_socket.is_open()) { + if (!socket.is_open()) { return HttpStatus("500", "FCGI connection", context.SetResponseHeader); } - if (opening) { - FCGI_Record get_values{FCGI_GET_VALUES, 0, system_config_bytes}; - if (m_socket.write_some(boost::asio::buffer(get_values.getBuffer())) != get_values.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 1" << std::endl; - } - FCGI_ID_Guard id_guard(m_fcgi_id); uint16_t id {id_guard.getID()}; - FCGI_Record begin_request{FCGI_BEGIN_REQUEST, id, FCGI_RESPONDER, FCGI_KEEP_CONN}; - if (m_socket.write_some(boost::asio::buffer(begin_request.getBuffer())) != begin_request.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 3" << std::endl; + try { + if (opening) { + FCGI_Record get_values{FCGI_GET_VALUES, 0, system_config_bytes}; + if (socket.write_some(boost::asio::buffer(get_values.getBuffer())) != get_values.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 1" << std::endl; + } + + FCGI_Record begin_request{FCGI_BEGIN_REQUEST, id, FCGI_RESPONDER, FCGI_KEEP_CONN}; + if (socket.write_some(boost::asio::buffer(begin_request.getBuffer())) != begin_request.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 3" << std::endl; - FCGI_Record params{FCGI_PARAMS, id, env_bytes}; - if (m_socket.write_some(boost::asio::buffer(params.getBuffer())) != params.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 4" << std::endl; + FCGI_Record params{FCGI_PARAMS, id, env_bytes}; + if (socket.write_some(boost::asio::buffer(params.getBuffer())) != params.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 4" << std::endl; - if (env_bytes.size()) { - FCGI_Record params_end{FCGI_PARAMS, id, std::string{}}; - if (m_socket.write_some(boost::asio::buffer(params_end.getBuffer())) != params_end.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 5" << std::endl; - } + if (env_bytes.size()) { + FCGI_Record params_end{FCGI_PARAMS, id, std::string{}}; + if (socket.write_some(boost::asio::buffer(params_end.getBuffer())) != params_end.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 5" << std::endl; + } - std::string body {context.GetRequestParam("body")}; - FCGI_Record stdin_{FCGI_STDIN, id, body}; - if (m_socket.write_some(boost::asio::buffer(stdin_.getBuffer())) != stdin_.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 6" << std::endl; - - if (body.size()) { - FCGI_Record stdin_end{FCGI_STDIN, id, std::string{}}; - if (m_socket.write_some(boost::asio::buffer(stdin_end.getBuffer())) != stdin_end.getBuffer().size()) - std::cerr << "Warning: Not all bytes written 7" << std::endl; + std::string body {context.GetRequestParam("body")}; + FCGI_Record stdin_{FCGI_STDIN, id, body}; + if (socket.write_some(boost::asio::buffer(stdin_.getBuffer())) != stdin_.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 6" << std::endl; + + if (body.size()) { + FCGI_Record stdin_end{FCGI_STDIN, id, std::string{}}; + if (socket.write_some(boost::asio::buffer(stdin_end.getBuffer())) != stdin_end.getBuffer().size()) + std::cerr << "Warning: Not all bytes written 7" << std::endl; + } + } catch (const boost::system::system_error& ex) { + if (ex.code() == boost::asio::error::eof) { + std::cerr << "FCGI Error: EOF on write" << std::endl; // seems to be ok here + return HttpStatus("500", "FCGI connection: EOF on write", context.SetResponseHeader); + } } #if 0 FCGI_Record data{FCGI_DATA, id, std::string{}}; - if (m_socket.write_some(boost::asio::buffer(data.getBuffer())) != data.getBuffer().size()) + if (socket.write_some(boost::asio::buffer(data.getBuffer())) != data.getBuffer().size()) std::cerr << "Warning: Not all bytes written 8" << std::endl; #endif @@ -405,11 +440,13 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) std::vector inbuf_part(1024); while (!ended) { try { - size_t got {m_socket.read_some(boost::asio::buffer(inbuf_part))}; + size_t got {socket.read_some(boost::asio::buffer(inbuf_part))}; inbuf.insert(inbuf.end(), inbuf_part.begin(), inbuf_part.begin() + got); } catch (const boost::system::system_error& ex) { if (ex.code() == boost::asio::error::eof) { //std::cerr << "FCGI Warning: Early EOF" << std::endl; // seems to be ok here + ended = true; + //return HttpStatus("500", "FCGI connection: EOF on read", context.SetResponseHeader); } else { std::cerr << "FCGI Warning: Expected EOF, got " << ex.code() << ", " << ex.what() << std::endl; ended = true; @@ -425,7 +462,7 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) } else if (r.getType() == FCGI_STDOUT) { output_data += r.getContent(); } else if (r.getType() == FCGI_STDERR) { - std::cerr << "FCGI STDERR: " << r.getContent(); + std::cerr << "FCGI STDERR: " << r.getContent() << std::endl; } else if (r.getType() == FCGI_GET_VALUES_RESULT) { FCGI_DecodeEnv(r.getContent(), app_values); DumpAppValues(app_values); @@ -437,6 +474,7 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) } } } + } else if (fs::is_socket(fs::path{app_addr})) { // Unix domain socket // TODO std::cerr << "FCGI Error: Unix domain sockets not yet implemented." << std::endl; @@ -496,7 +534,6 @@ std::string fcgi_plugin::name() fcgi_plugin::fcgi_plugin() : m_io_context() , m_resolver(m_io_context) - , m_socket(m_io_context) { //std::cout << "Plugin constructor" << std::endl; } diff --git a/plugins/fcgi/fcgi.h b/plugins/fcgi/fcgi.h index b881aec..4f77719 100644 --- a/plugins/fcgi/fcgi.h +++ b/plugins/fcgi/fcgi.h @@ -62,7 +62,7 @@ class fcgi_plugin: public webserver_plugin_interface boost::asio::ip::tcp::resolver m_resolver; std::mutex m_socket_mutex; // guard m_socket use in different threads - boost::asio::ip::tcp::socket m_socket; + std::unordered_map m_sockets; public: fcgi_plugin(); diff --git a/plugins/statistics/statistics.cpp b/plugins/statistics/statistics.cpp index 35ea241..6d3899e 100644 --- a/plugins/statistics/statistics.cpp +++ b/plugins/statistics/statistics.cpp @@ -113,7 +113,7 @@ std::string statistics_plugin::generate_page( result += "

Webserver Statistics

"; result += "

Host uptime: "s + GetServerParam("uptime_host") + "

"; - result += "

Host webserver: "s + GetServerParam("uptime_webserver") + "

"; + result += "

Webserver uptime: "s + GetServerParam("uptime_webserver") + "

"; result += "

IPv6 fraction by requests: "s + std::to_string(ipv6_fraction_by_requests * 100) + "%

"; result += "

IPv6 fraction by bytes: "s + std::to_string(ipv6_fraction_by_bytes * 100) + "%

"; result += "

HTTPS fraction by requests: "s + std::to_string(https_fraction_by_requests * 100) + "%

"; -- cgit v1.2.3