Loading config/proxyplus.yaml +4 −0 Changes for config/proxyplus.yaml: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ proxyplus: # once; idle_timeout_seconds discards (rather than reuses) one that's sat idle longer. upstream_pool_max_idle_per_origin: 8 upstream_pool_idle_timeout_seconds: 60 # Bounds both the TCP/TLS connect attempt and the wait for the upstream's response for # every proxied request. Raise this for a legitimately slow-but-alive upstream (a # cold-starting backend, a long-running API call, ...) that needs more than 60s. upstream_request_timeout_seconds: 60 pid_file: "/run/proxyplus.pid" recovery_mode: false Loading src/config/BootstrapConfig.cpp +3 −0 Changes for src/config/BootstrapConfig.cpp: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ const std::set<std::string> &knownLeafPatterns() { "/proxyplus/process/idle_connection_timeout_seconds", "/proxyplus/process/upstream_pool_max_idle_per_origin", "/proxyplus/process/upstream_pool_idle_timeout_seconds", "/proxyplus/process/upstream_request_timeout_seconds", "/proxyplus/pid_file", "/proxyplus/recovery_mode", "/proxyplus/maintenance_mode", Loading Loading @@ -232,6 +233,8 @@ void BootstrapConfig::loadAndValidate() { readInt(conf, "/proxyplus/process/upstream_pool_max_idle_per_origin", 8); cfg.upstreamPoolIdleTimeoutSeconds = readInt(conf, "/proxyplus/process/upstream_pool_idle_timeout_seconds", 60); cfg.upstreamRequestTimeoutSeconds = readInt(conf, "/proxyplus/process/upstream_request_timeout_seconds", 60); cfg.pidFile = readValue(conf, "/proxyplus/pid_file", ""); cfg.recoveryMode = readBool(conf, "/proxyplus/recovery_mode", false) ? RecoveryMode::Recover Loading src/config/BootstrapConfig.h +9 −0 Changes for src/config/BootstrapConfig.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -110,6 +110,15 @@ struct AppConfig { int upstreamPoolMaxIdlePerOrigin = 8; int upstreamPoolIdleTimeoutSeconds = 60; // Bounds HttpClient's connect attempt AND its wait for the upstream's response (see // libhttppp::HttpClient's timeoutSec ctor param / setTimeout()) for every proxied // request -- previously a literal 60 hardcoded at the one call site // (HttpProxyController::RequestEvent), which meant a legitimately slow-but-alive // upstream (a cold-starting backend, a long-running API call, ...) had no way to get // more headroom without a code change. 60s default matches that prior hardcoded value, // so an unset config keeps today's behavior. int upstreamRequestTimeoutSeconds = 60; RecoveryMode recoveryMode = RecoveryMode::Normal; MaintenanceMode maintenanceMode = MaintenanceMode::Off; }; Loading src/http/HttpProxyServer.cpp +14 −8 Changes for src/http/HttpProxyServer.cpp: 14 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -116,7 +116,8 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) // H2 dispatch now runs through the same offload pool as H1 (see shouldOffloadH2Dispatch // below): both RequestEvent calls do real backend I/O, so both need off the shared // epoll/kqueue listener workers to avoid one bursty connection starving new TLS Loading @@ -136,7 +137,8 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo , _pluginManager(pluginManager) , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) { , _acmeChallenges(acmeChallenges) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { } bool HttpProxyController::shouldOffloadH2Dispatch(libhttppp::HttpRequest &, uint32_t) const { Loading Loading @@ -505,7 +507,7 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int // falling back to HTTP/2 then HTTP/1.1 over TLS (or straight to H1 for a cleartext // upstream) when a new connection actually needs constructing -- see HttpClient // ctor in libhttppp/src/http.cpp for the fallback ladder. auto clientPtr = _connectionPool.acquire(upstream, /*vers=*/3, /*timeoutSec=*/60, auto clientPtr = _connectionPool.acquire(upstream, /*vers=*/3, _upstreamTimeoutSeconds, trustPolicyFor(matched)); libhttppp::HttpClient &client = *clientPtr; poolGuard.arm(_connectionPool, upstream, std::move(clientPtr)); Loading Loading @@ -660,7 +662,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, std::string ce lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) : _listener(listener) , _tlsActive(!listener.tlsProfile.empty()) , _httpd(std::make_unique<libhttppp::HttpD>(listener.address, listener.port, 512, certPath, Loading @@ -671,7 +674,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, std::string ce , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) , _idleTimeoutSeconds(idleTimeoutSeconds) { , _idleTimeoutSeconds(idleTimeoutSeconds) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { bindAndListen(); } Loading @@ -681,7 +685,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, plugin::PluginManager &pluginManager, lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) : _listener(listener) , _tlsActive(true) , _httpd(std::make_unique<libhttppp::HttpD>(listener.address, listener.port, 512, certsByHostname)) Loading @@ -691,7 +696,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) , _idleTimeoutSeconds(idleTimeoutSeconds) { , _idleTimeoutSeconds(idleTimeoutSeconds) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { bindAndListen(); } Loading @@ -711,7 +717,7 @@ void HttpProxyServer::run() { _listener.address + ":" + std::to_string(_listener.port), _tlsActive, _configStore, _authService, _pluginManager, _loadBalancer, _connectionPool, _acmeChallenges, _idleTimeoutSeconds); _idleTimeoutSeconds, _upstreamTimeoutSeconds); controller.runEventloop(); } Loading src/http/HttpProxyServer.h +14 −5 Changes for src/http/HttpProxyServer.h: 14 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -29,6 +29,10 @@ public: // idleTimeoutSeconds is forwarded straight to the libhttppp::HttpEvent base (see its own // doc comment in httpd.h) -- default 0 keeps today's behavior for any caller that doesn't // pass it explicitly; main.cpp's real call sites pass AppConfig::idleConnectionTimeoutSeconds. // upstreamTimeoutSeconds bounds each proxied request's own connect+response wait (see // UpstreamConnectionPool::acquire()'s timeoutSec) -- default 60 matches the value that used // to be hardcoded at the RequestEvent call site; main.cpp's real call sites pass // AppConfig::upstreamRequestTimeoutSeconds. HttpProxyController(std::vector<netplus::socket *> serverSockets, std::string listenerLabel, bool tlsActive, Loading @@ -38,7 +42,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); void RequestEvent(libhttppp::HttpRequest &curreq, const int tid, ULONG_PTR args) override; Loading Loading @@ -99,6 +104,7 @@ private: lb::LoadBalancer &_loadBalancer; http::UpstreamConnectionPool &_connectionPool; tls::AcmeChallengeStore *_acmeChallenges; int _upstreamTimeoutSeconds; }; // Binds the listener socket(s) *at construction time* (see main.cpp / spec "Privilege Loading @@ -108,8 +114,8 @@ private: // from a worker thread after privileges have been dropped. class HttpProxyServer { public: // idleTimeoutSeconds: see HttpProxyController's own doc comment above -- threaded down // into the controller it constructs internally in run(). // idleTimeoutSeconds/upstreamTimeoutSeconds: see HttpProxyController's own doc comment // above -- threaded down into the controller it constructs internally in run(). HttpProxyServer(config::ListenerConfig listener, std::string certPath, std::string keyPath, Loading @@ -119,7 +125,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); // SNI virtual hosting variant: certsByHostname carries one CertificateBundle per domain // sharing this listener (see ListenerConfig::tlsProfiles) instead of the single cert/key Loading @@ -132,7 +139,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); void run(); Loading Loading @@ -161,6 +169,7 @@ private: http::UpstreamConnectionPool &_connectionPool; tls::AcmeChallengeStore *_acmeChallenges; int _idleTimeoutSeconds; int _upstreamTimeoutSeconds; }; } // namespace proxyplus::httpproxy Loading
config/proxyplus.yaml +4 −0 Changes for config/proxyplus.yaml: 4 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -77,6 +77,10 @@ proxyplus: # once; idle_timeout_seconds discards (rather than reuses) one that's sat idle longer. upstream_pool_max_idle_per_origin: 8 upstream_pool_idle_timeout_seconds: 60 # Bounds both the TCP/TLS connect attempt and the wait for the upstream's response for # every proxied request. Raise this for a legitimately slow-but-alive upstream (a # cold-starting backend, a long-running API call, ...) that needs more than 60s. upstream_request_timeout_seconds: 60 pid_file: "/run/proxyplus.pid" recovery_mode: false Loading
src/config/BootstrapConfig.cpp +3 −0 Changes for src/config/BootstrapConfig.cpp: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ const std::set<std::string> &knownLeafPatterns() { "/proxyplus/process/idle_connection_timeout_seconds", "/proxyplus/process/upstream_pool_max_idle_per_origin", "/proxyplus/process/upstream_pool_idle_timeout_seconds", "/proxyplus/process/upstream_request_timeout_seconds", "/proxyplus/pid_file", "/proxyplus/recovery_mode", "/proxyplus/maintenance_mode", Loading Loading @@ -232,6 +233,8 @@ void BootstrapConfig::loadAndValidate() { readInt(conf, "/proxyplus/process/upstream_pool_max_idle_per_origin", 8); cfg.upstreamPoolIdleTimeoutSeconds = readInt(conf, "/proxyplus/process/upstream_pool_idle_timeout_seconds", 60); cfg.upstreamRequestTimeoutSeconds = readInt(conf, "/proxyplus/process/upstream_request_timeout_seconds", 60); cfg.pidFile = readValue(conf, "/proxyplus/pid_file", ""); cfg.recoveryMode = readBool(conf, "/proxyplus/recovery_mode", false) ? RecoveryMode::Recover Loading
src/config/BootstrapConfig.h +9 −0 Changes for src/config/BootstrapConfig.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -110,6 +110,15 @@ struct AppConfig { int upstreamPoolMaxIdlePerOrigin = 8; int upstreamPoolIdleTimeoutSeconds = 60; // Bounds HttpClient's connect attempt AND its wait for the upstream's response (see // libhttppp::HttpClient's timeoutSec ctor param / setTimeout()) for every proxied // request -- previously a literal 60 hardcoded at the one call site // (HttpProxyController::RequestEvent), which meant a legitimately slow-but-alive // upstream (a cold-starting backend, a long-running API call, ...) had no way to get // more headroom without a code change. 60s default matches that prior hardcoded value, // so an unset config keeps today's behavior. int upstreamRequestTimeoutSeconds = 60; RecoveryMode recoveryMode = RecoveryMode::Normal; MaintenanceMode maintenanceMode = MaintenanceMode::Off; }; Loading
src/http/HttpProxyServer.cpp +14 −8 Changes for src/http/HttpProxyServer.cpp: 14 added lines, 8 removed lines. Original line number Diff line number Diff line Loading @@ -116,7 +116,8 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) // H2 dispatch now runs through the same offload pool as H1 (see shouldOffloadH2Dispatch // below): both RequestEvent calls do real backend I/O, so both need off the shared // epoll/kqueue listener workers to avoid one bursty connection starving new TLS Loading @@ -136,7 +137,8 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo , _pluginManager(pluginManager) , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) { , _acmeChallenges(acmeChallenges) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { } bool HttpProxyController::shouldOffloadH2Dispatch(libhttppp::HttpRequest &, uint32_t) const { Loading Loading @@ -505,7 +507,7 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int // falling back to HTTP/2 then HTTP/1.1 over TLS (or straight to H1 for a cleartext // upstream) when a new connection actually needs constructing -- see HttpClient // ctor in libhttppp/src/http.cpp for the fallback ladder. auto clientPtr = _connectionPool.acquire(upstream, /*vers=*/3, /*timeoutSec=*/60, auto clientPtr = _connectionPool.acquire(upstream, /*vers=*/3, _upstreamTimeoutSeconds, trustPolicyFor(matched)); libhttppp::HttpClient &client = *clientPtr; poolGuard.arm(_connectionPool, upstream, std::move(clientPtr)); Loading Loading @@ -660,7 +662,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, std::string ce lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) : _listener(listener) , _tlsActive(!listener.tlsProfile.empty()) , _httpd(std::make_unique<libhttppp::HttpD>(listener.address, listener.port, 512, certPath, Loading @@ -671,7 +674,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, std::string ce , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) , _idleTimeoutSeconds(idleTimeoutSeconds) { , _idleTimeoutSeconds(idleTimeoutSeconds) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { bindAndListen(); } Loading @@ -681,7 +685,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, plugin::PluginManager &pluginManager, lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) int idleTimeoutSeconds, int upstreamTimeoutSeconds) : _listener(listener) , _tlsActive(true) , _httpd(std::make_unique<libhttppp::HttpD>(listener.address, listener.port, 512, certsByHostname)) Loading @@ -691,7 +696,8 @@ HttpProxyServer::HttpProxyServer(config::ListenerConfig listener, , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) , _idleTimeoutSeconds(idleTimeoutSeconds) { , _idleTimeoutSeconds(idleTimeoutSeconds) , _upstreamTimeoutSeconds(upstreamTimeoutSeconds) { bindAndListen(); } Loading @@ -711,7 +717,7 @@ void HttpProxyServer::run() { _listener.address + ":" + std::to_string(_listener.port), _tlsActive, _configStore, _authService, _pluginManager, _loadBalancer, _connectionPool, _acmeChallenges, _idleTimeoutSeconds); _idleTimeoutSeconds, _upstreamTimeoutSeconds); controller.runEventloop(); } Loading
src/http/HttpProxyServer.h +14 −5 Changes for src/http/HttpProxyServer.h: 14 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -29,6 +29,10 @@ public: // idleTimeoutSeconds is forwarded straight to the libhttppp::HttpEvent base (see its own // doc comment in httpd.h) -- default 0 keeps today's behavior for any caller that doesn't // pass it explicitly; main.cpp's real call sites pass AppConfig::idleConnectionTimeoutSeconds. // upstreamTimeoutSeconds bounds each proxied request's own connect+response wait (see // UpstreamConnectionPool::acquire()'s timeoutSec) -- default 60 matches the value that used // to be hardcoded at the RequestEvent call site; main.cpp's real call sites pass // AppConfig::upstreamRequestTimeoutSeconds. HttpProxyController(std::vector<netplus::socket *> serverSockets, std::string listenerLabel, bool tlsActive, Loading @@ -38,7 +42,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); void RequestEvent(libhttppp::HttpRequest &curreq, const int tid, ULONG_PTR args) override; Loading Loading @@ -99,6 +104,7 @@ private: lb::LoadBalancer &_loadBalancer; http::UpstreamConnectionPool &_connectionPool; tls::AcmeChallengeStore *_acmeChallenges; int _upstreamTimeoutSeconds; }; // Binds the listener socket(s) *at construction time* (see main.cpp / spec "Privilege Loading @@ -108,8 +114,8 @@ private: // from a worker thread after privileges have been dropped. class HttpProxyServer { public: // idleTimeoutSeconds: see HttpProxyController's own doc comment above -- threaded down // into the controller it constructs internally in run(). // idleTimeoutSeconds/upstreamTimeoutSeconds: see HttpProxyController's own doc comment // above -- threaded down into the controller it constructs internally in run(). HttpProxyServer(config::ListenerConfig listener, std::string certPath, std::string keyPath, Loading @@ -119,7 +125,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); // SNI virtual hosting variant: certsByHostname carries one CertificateBundle per domain // sharing this listener (see ListenerConfig::tlsProfiles) instead of the single cert/key Loading @@ -132,7 +139,8 @@ public: lb::LoadBalancer &loadBalancer, http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges = nullptr, int idleTimeoutSeconds = 0); int idleTimeoutSeconds = 0, int upstreamTimeoutSeconds = 60); void run(); Loading Loading @@ -161,6 +169,7 @@ private: http::UpstreamConnectionPool &_connectionPool; tls::AcmeChallengeStore *_acmeChallenges; int _idleTimeoutSeconds; int _upstreamTimeoutSeconds; }; } // namespace proxyplus::httpproxy