From 4b34a4d950f762b5d020d5ac4d3354836833039c Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Thu, 21 May 2020 18:00:25 +0200 Subject: FCGI: Fix connection handling on broken pipe: Reopen new socket --- plugins/fcgi/fcgi.cpp | 10 ++++++++-- plugins/fcgi/socket.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++------ plugins/fcgi/socket.h | 6 ++++++ 3 files changed, 57 insertions(+), 8 deletions(-) (limited to 'plugins/fcgi') diff --git a/plugins/fcgi/fcgi.cpp b/plugins/fcgi/fcgi.cpp index 5a7ce65..7748845 100644 --- a/plugins/fcgi/fcgi.cpp +++ b/plugins/fcgi/fcgi.cpp @@ -357,7 +357,7 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) std::lock_guard socket_lock{socket->getMutex()}; if (!socket->is_open()) { - //std::cout << "FCGI: Opening new socket" << std::endl; + std::cout << "FCGI: Opening new socket to " << app_addr << std::endl; socket->open(); opening = true; @@ -402,7 +402,12 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) std::cerr << "Warning: Not all bytes written" << std::endl; } catch (const fcgi_eof_error&) { std::cerr << "FCGI Error: EOF on write" << std::endl; // seems to be ok here + m_sockets.erase(app_addr); // force new socket next time return HttpStatus("500", "FCGI connection: EOF on write", context.SetResponseHeader); + } catch (const fcgi_broken_pipe_error&) { + std::cerr << "FCGI Error: Broken pipe on write" << std::endl; // seems to be ok here + m_sockets.erase(app_addr); // force new socket next time + return HttpStatus("500", "FCGI connection: Broken pipe on write", context.SetResponseHeader); } #if 0 @@ -418,7 +423,8 @@ std::string fcgi_plugin::fcgiQuery(FCGIContext& context) try { socket->read(inbuf); } catch (const fcgi_eof_error&) { - std::cerr << "FCGI Warning: Early EOF" << std::endl; // seems to be ok here + std::cerr << "FCGI Warning: Early EOF from application server. Break." << std::endl; // seems to be ok here + m_sockets.erase(app_addr); // force new socket next time ended = true; //return HttpStatus("500", "FCGI connection: EOF on read", context.SetResponseHeader); } diff --git a/plugins/fcgi/socket.cpp b/plugins/fcgi/socket.cpp index badcec6..82bb06b 100644 --- a/plugins/fcgi/socket.cpp +++ b/plugins/fcgi/socket.cpp @@ -81,11 +81,44 @@ void TCPSocket::open() bool TCPSocket::is_open() { - return m_socket.is_open(); + if (m_socket.is_open()) { + std::vector inbuf; + + int error; + socklen_t len = sizeof (error); + int retval {getsockopt(m_socket.native_handle(), SOL_SOCKET, SO_ERROR, &error, &len)}; + + if (retval) { + std::cerr << "FCGI Error: getsockopt() error in is_open(): " << strerror(errno) << std::endl; + try { + // for graceful shutdown, according to + // https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/basic_stream_socket/close/overload1.html + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_socket.close(); // correct state + } catch (...) { + std::cerr << "Error on shutdown/close" << std::endl; + } + return false; + } + + if (error != 0) { + try { + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + m_socket.close(); // correct state + } catch (...) { + std::cerr << "Error on shutdown/close" << std::endl; + } + return false; + } + + return true; + } else + return false; } void TCPSocket::close() { + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); m_socket.close(); } @@ -96,6 +129,8 @@ size_t TCPSocket::write(const std::vector& data) } catch (const boost::system::system_error& ex) { if (ex.code() == boost::asio::error::eof) { throw fcgi_eof_error("EOF on write"); + } else if (ex.code() == boost::asio::error::broken_pipe) { + throw fcgi_broken_pipe_error("Application server connection broken"); } else throw std::runtime_error("FCGI Error: Unknown boost asio exception on write: "s + ex.what()); } catch (const std::exception& ex) { @@ -108,12 +143,12 @@ size_t TCPSocket::read(std::vector& data) try { size_t result{0}; - while (m_socket.available()) { + do { std::vector inbuf_part(1024); size_t got { m_socket.read_some(boost::asio::buffer(inbuf_part))}; data.insert(data.end(), inbuf_part.begin(), inbuf_part.begin() + got); result += got; - } + } while (m_socket.available()); return result; @@ -165,6 +200,8 @@ size_t FileSocket::write(const std::vector& data) } catch (const boost::system::system_error& ex) { if (ex.code() == boost::asio::error::eof) { throw fcgi_eof_error("EOF on write"); + } else if (ex.code() == boost::asio::error::broken_pipe) { + throw fcgi_broken_pipe_error("Application server connection broken"); } else throw std::runtime_error("FCGI Error: Unknown boost asio exception on write: "s + ex.what()); } catch (const std::exception& ex) { @@ -176,13 +213,13 @@ size_t FileSocket::read(std::vector& data) { try { size_t result{0}; - - while (m_socket.available()) { + + do { std::vector inbuf_part(1024); size_t got { m_socket.read_some(boost::asio::buffer(inbuf_part))}; data.insert(data.end(), inbuf_part.begin(), inbuf_part.begin() + got); result += got; - } + } while (m_socket.available()); return result; } catch (const boost::system::system_error& ex) { diff --git a/plugins/fcgi/socket.h b/plugins/fcgi/socket.h index 8fb5610..272b844 100644 --- a/plugins/fcgi/socket.h +++ b/plugins/fcgi/socket.h @@ -15,6 +15,12 @@ public: fcgi_eof_error(const std::string& what_arg): std::runtime_error(what_arg) {} }; +class fcgi_broken_pipe_error: public std::runtime_error +{ +public: + fcgi_broken_pipe_error(const std::string& what_arg): std::runtime_error(what_arg) {} +}; + class Socket { std::mutex m_mutex; // guard socket use in different threads -- cgit v1.2.3