Loading src/http/HttpProxyServer.cpp +23 −10 Changes for src/http/HttpProxyServer.cpp: 23 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -117,15 +117,13 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) // Keep H2 dispatch synchronous for now. Enabling libhttppp's H2 offload pool makes an // otherwise healthy proxied response remain queued after RequestEvent returns: the client // completes TLS/ALPN and sends its stream, but receives no response frames. HTTP/1.1 // continues to work against the same route/upstream, which makes this especially damaging // for browsers (they normally negotiate h2). H1 offloading is independent and remains // enabled so slow backend round-trips and WebSocket handshakes do not occupy the shared // listener workers. Re-enable H2 offloading only after its response handoff/flush path has // an integration test that observes the response on the client socket. : libhttppp::HttpEvent(std::move(serverSockets), /*timeout=*/1000, // Keep H2 dispatch disabled for now. Enabling libhttppp's H2 offload pool in the deployed // stack makes an otherwise healthy proxied response remain queued after RequestEvent // returns, while synchronous H2 lets a frontend request burst occupy every listener worker // and prevents even new TLS handshakes. The constructor body therefore also removes h2 // from TLS ALPN instead of merely leaving these requests synchronous. Clients use the // independently-offloaded H1 path initially and can still discover/use H3 via Alt-Svc. : libhttppp::HttpEvent(serverSockets, /*timeout=*/1000, /*h2OffloadThreads=*/0, idleTimeoutSeconds, /*h1OffloadThreads=*/(std::max<size_t>)(4, std::thread::hardware_concurrency() * 2)) Loading @@ -136,7 +134,22 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo , _pluginManager(pluginManager) , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) {} , _acmeChallenges(acmeChallenges) { // HttpEvent advertises h2 by default on every TLS-over-TCP listener. Do not advertise a // protocol whose dispatch mode is intentionally disabled above: browsers otherwise choose // h2 and can starve the shared listener workers before their WebSocket connection is even // accepted. QUIC sockets are separate and retain their h3 ALPN configuration. for (auto *sock : serverSockets) { if (auto *tlsSock = dynamic_cast<netplus::ssl *>(sock)) { tlsSock->setAlpnSelectCallback([](const std::vector<std::string> &protocols) { for (const auto &protocol : protocols) { if (protocol == "http/1.1") return protocol; } return std::string{}; }); } } } bool HttpProxyController::tryServeAcmeChallenge(libhttppp::HttpRequest &curreq, const std::string &url) { Loading Loading
src/http/HttpProxyServer.cpp +23 −10 Changes for src/http/HttpProxyServer.cpp: 23 added lines, 10 removed lines. Original line number Diff line number Diff line Loading @@ -117,15 +117,13 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo http::UpstreamConnectionPool &connectionPool, tls::AcmeChallengeStore *acmeChallenges, int idleTimeoutSeconds) // Keep H2 dispatch synchronous for now. Enabling libhttppp's H2 offload pool makes an // otherwise healthy proxied response remain queued after RequestEvent returns: the client // completes TLS/ALPN and sends its stream, but receives no response frames. HTTP/1.1 // continues to work against the same route/upstream, which makes this especially damaging // for browsers (they normally negotiate h2). H1 offloading is independent and remains // enabled so slow backend round-trips and WebSocket handshakes do not occupy the shared // listener workers. Re-enable H2 offloading only after its response handoff/flush path has // an integration test that observes the response on the client socket. : libhttppp::HttpEvent(std::move(serverSockets), /*timeout=*/1000, // Keep H2 dispatch disabled for now. Enabling libhttppp's H2 offload pool in the deployed // stack makes an otherwise healthy proxied response remain queued after RequestEvent // returns, while synchronous H2 lets a frontend request burst occupy every listener worker // and prevents even new TLS handshakes. The constructor body therefore also removes h2 // from TLS ALPN instead of merely leaving these requests synchronous. Clients use the // independently-offloaded H1 path initially and can still discover/use H3 via Alt-Svc. : libhttppp::HttpEvent(serverSockets, /*timeout=*/1000, /*h2OffloadThreads=*/0, idleTimeoutSeconds, /*h1OffloadThreads=*/(std::max<size_t>)(4, std::thread::hardware_concurrency() * 2)) Loading @@ -136,7 +134,22 @@ HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSo , _pluginManager(pluginManager) , _loadBalancer(loadBalancer) , _connectionPool(connectionPool) , _acmeChallenges(acmeChallenges) {} , _acmeChallenges(acmeChallenges) { // HttpEvent advertises h2 by default on every TLS-over-TCP listener. Do not advertise a // protocol whose dispatch mode is intentionally disabled above: browsers otherwise choose // h2 and can starve the shared listener workers before their WebSocket connection is even // accepted. QUIC sockets are separate and retain their h3 ALPN configuration. for (auto *sock : serverSockets) { if (auto *tlsSock = dynamic_cast<netplus::ssl *>(sock)) { tlsSock->setAlpnSelectCallback([](const std::vector<std::string> &protocols) { for (const auto &protocol : protocols) { if (protocol == "http/1.1") return protocol; } return std::string{}; }); } } } bool HttpProxyController::tryServeAcmeChallenge(libhttppp::HttpRequest &curreq, const std::string &url) { Loading