Loading src/http/WebSocketProxy.cpp +22 −0 Changes for src/http/WebSocketProxy.cpp: 22 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -234,25 +234,47 @@ void relayLoop(std::unique_ptr<netplus::socket> client, time_t lastActivity = std::time(nullptr); for (;;) { // A TLS socket may already hold one or more decrypted records in its own receive // buffer even though the kernel fd is no longer readable. Waiting only in // socketwait in that state strands those records forever: no new edge has to // arrive, and Home Assistant's tiny auth_ok/auth_invalid frame is a particularly // easy victim. Check transport-owned buffering before polling the fds. ready.assign(socks.size(), false); ready[0] = client->hasBufferedData(); ready[1] = upstream->hasBufferedData(); if (!ready[0] && !ready[1]) { int n = sw.waitReadMulti(socks, ready, /*timeout_ms=*/60000); if (n <= 0) { if (std::time(nullptr) - lastActivity > kWsIdleTimeoutSeconds) break; continue; // timeout -- keep waiting, WebSocket pings keep this alive } } if (ready[0]) { try { netplus::buffer buf(16384); size_t r = client->recvData(buf, 0); if (r == 0) break; // client closed sendAll(*upstream, std::string(buf.data.buf, r)); lastActivity = std::time(nullptr); } catch (const netplus::NetException &e) { // TLS recvData() uses Note for a live connection whose readable record was // control traffic (e.g. KeyUpdate/NewSessionTicket) rather than application // data. That is a retry condition, not a tunnel disconnect. if (e.getErrorType() != netplus::NetException::Note) break; } } if (ready[1]) { try { netplus::buffer buf(16384); size_t r = upstream->recvData(buf, 0); if (r == 0) break; // upstream closed sendAll(*client, std::string(buf.data.buf, r)); lastActivity = std::time(nullptr); } catch (const netplus::NetException &e) { if (e.getErrorType() != netplus::NetException::Note) break; } } } } catch (const netplus::NetException &) { Loading Loading
src/http/WebSocketProxy.cpp +22 −0 Changes for src/http/WebSocketProxy.cpp: 22 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -234,25 +234,47 @@ void relayLoop(std::unique_ptr<netplus::socket> client, time_t lastActivity = std::time(nullptr); for (;;) { // A TLS socket may already hold one or more decrypted records in its own receive // buffer even though the kernel fd is no longer readable. Waiting only in // socketwait in that state strands those records forever: no new edge has to // arrive, and Home Assistant's tiny auth_ok/auth_invalid frame is a particularly // easy victim. Check transport-owned buffering before polling the fds. ready.assign(socks.size(), false); ready[0] = client->hasBufferedData(); ready[1] = upstream->hasBufferedData(); if (!ready[0] && !ready[1]) { int n = sw.waitReadMulti(socks, ready, /*timeout_ms=*/60000); if (n <= 0) { if (std::time(nullptr) - lastActivity > kWsIdleTimeoutSeconds) break; continue; // timeout -- keep waiting, WebSocket pings keep this alive } } if (ready[0]) { try { netplus::buffer buf(16384); size_t r = client->recvData(buf, 0); if (r == 0) break; // client closed sendAll(*upstream, std::string(buf.data.buf, r)); lastActivity = std::time(nullptr); } catch (const netplus::NetException &e) { // TLS recvData() uses Note for a live connection whose readable record was // control traffic (e.g. KeyUpdate/NewSessionTicket) rather than application // data. That is a retry condition, not a tunnel disconnect. if (e.getErrorType() != netplus::NetException::Note) break; } } if (ready[1]) { try { netplus::buffer buf(16384); size_t r = upstream->recvData(buf, 0); if (r == 0) break; // upstream closed sendAll(*client, std::string(buf.data.buf, r)); lastActivity = std::time(nullptr); } catch (const netplus::NetException &e) { if (e.getErrorType() != netplus::NetException::Note) break; } } } } catch (const netplus::NetException &) { Loading