diff options
| author | Roland Reichwein <mail@reichwein.it> | 2020-05-03 22:52:45 +0200 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2020-05-03 22:52:45 +0200 | 
| commit | 1cc484b25547e349177cf652f62021b802f48655 (patch) | |
| tree | 9f07ef0a57aa145943df8e2c200b48f9b9a48a32 | |
| parent | f60f8e469cf8c2aff2cf62e42a46ad806e663d41 (diff) | |
Fixed TCP keepalive (workaround)
| -rw-r--r-- | debian/control | 1 | ||||
| -rw-r--r-- | plugins/fcgi/fcgi.cpp | 105 | ||||
| -rw-r--r-- | plugins/fcgi/fcgi.h | 2 | ||||
| -rw-r--r-- | 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<std::string, std::string> 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<std::mutex> socket_lock{m_socket_mutex}; -  if (!m_socket.is_open()) { +  auto it {m_sockets.find(app_addr)}; + +  std::pair<std::unordered_map<std::string, boost::asio::ip::tcp::socket>::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<char> 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<std::string, boost::asio::ip::tcp::socket> 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 += "<h1>Webserver Statistics</h1>";     result += "<p>Host uptime: "s + GetServerParam("uptime_host") + "</p>"; -   result += "<p>Host webserver: "s + GetServerParam("uptime_webserver") + "</p>"; +   result += "<p>Webserver uptime: "s + GetServerParam("uptime_webserver") + "</p>";     result += "<p>IPv6 fraction by requests: "s + std::to_string(ipv6_fraction_by_requests * 100) + "%</p>";     result += "<p>IPv6 fraction by bytes: "s + std::to_string(ipv6_fraction_by_bytes * 100) + "%</p>";     result += "<p>HTTPS fraction by requests: "s + std::to_string(https_fraction_by_requests * 100) + "%</p>";  | 
