Loading src/httpd.cpp +33 −6 Changes for src/httpd.cpp: 33 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -398,6 +398,21 @@ bool libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, << " activeAlready=" << had_active << std::endl; // Seed this stream's flow-control window now, synchronously, while we // hold the connection's event_mutex (see epoll.cpp's per-event lock // around dispatch). Offloaded requests hand the actual response off to // a background pool and don't call _finishH2Dispatch — which is what // normally sets this up — until the render completes; a fast peer can // send a WINDOW_UPDATE for this stream before that happens. Without an // entry here yet, that WINDOW_UPDATE finds nothing in peerStreamWindows // to credit (see the H2_FRAME_WINDOW_UPDATE case below) and is silently // discarded — permanently, since a well-behaved peer never repeats a // credit grant it believes already landed. The stream is then capped at // whatever the default window covers and stalls forever once that's // exhausted, with no further WINDOW_UPDATE ever coming to unstick it. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // Store POST body in RecvData so HttpForm::parse() can access it if (!reqBody.empty()) { tempreq.RecvData.append(reqBody.data(), reqBody.size()); Loading Loading @@ -503,9 +518,13 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, tempreq.h2state().expectedContentLength = content_length; tempreq.SendData.pos = 0; // Initialise per-stream peer window cureq.h2state().peerStreamWindows[sid] = cureq.h2state().peerInitialStreamWindow; // Per-stream peer window was already seeded in // _dispatchH2Stream. Don't overwrite it here — a WINDOW_UPDATE // for this stream may have already arrived (and been credited // to that entry) while an offloaded render was in flight; an // unconditional reassignment here would silently discard it. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // Collect extra :res-* headers std::vector<libhttppp::hpack::HeaderField> extra; Loading Loading @@ -634,9 +653,12 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, std::string hpack_out = libhttppp::hpack::Encoder::encodeResponseHeaders( status_code, content_type, body.size(), extra); // Initialise per-stream peer window for flow control cureq.h2state().peerStreamWindows[sid] = cureq.h2state().peerInitialStreamWindow; // Per-stream peer window was already seeded in _dispatchH2Stream — // see the comment there. Don't overwrite it here; it may already // carry credit from a WINDOW_UPDATE that arrived while an offloaded // render was in flight. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // HEADERS frame uint8_t hdr_flags = H2_FLAG_END_HEADERS; Loading Loading @@ -689,6 +711,11 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, uint8_t rst[4] = {0, 0, 0, 0x02}; // INTERNAL_ERROR out += h2BuildFrame(H2_FRAME_RST_STREAM, 0, sid, std::string(reinterpret_cast<char*>(rst), 4)); // This stream's window (seeded in _dispatchH2Stream) never got // used since no response body was ever sent — drop it rather than // leaking one stale entry per failed dispatch on a long-lived // connection. cureq.h2state().peerStreamWindows.erase(sid); } } Loading Loading
src/httpd.cpp +33 −6 Changes for src/httpd.cpp: 33 added lines, 6 removed lines. Original line number Diff line number Diff line Loading @@ -398,6 +398,21 @@ bool libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, << " activeAlready=" << had_active << std::endl; // Seed this stream's flow-control window now, synchronously, while we // hold the connection's event_mutex (see epoll.cpp's per-event lock // around dispatch). Offloaded requests hand the actual response off to // a background pool and don't call _finishH2Dispatch — which is what // normally sets this up — until the render completes; a fast peer can // send a WINDOW_UPDATE for this stream before that happens. Without an // entry here yet, that WINDOW_UPDATE finds nothing in peerStreamWindows // to credit (see the H2_FRAME_WINDOW_UPDATE case below) and is silently // discarded — permanently, since a well-behaved peer never repeats a // credit grant it believes already landed. The stream is then capped at // whatever the default window covers and stalls forever once that's // exhausted, with no further WINDOW_UPDATE ever coming to unstick it. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // Store POST body in RecvData so HttpForm::parse() can access it if (!reqBody.empty()) { tempreq.RecvData.append(reqBody.data(), reqBody.size()); Loading Loading @@ -503,9 +518,13 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, tempreq.h2state().expectedContentLength = content_length; tempreq.SendData.pos = 0; // Initialise per-stream peer window cureq.h2state().peerStreamWindows[sid] = cureq.h2state().peerInitialStreamWindow; // Per-stream peer window was already seeded in // _dispatchH2Stream. Don't overwrite it here — a WINDOW_UPDATE // for this stream may have already arrived (and been credited // to that entry) while an offloaded render was in flight; an // unconditional reassignment here would silently discard it. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // Collect extra :res-* headers std::vector<libhttppp::hpack::HeaderField> extra; Loading Loading @@ -634,9 +653,12 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, std::string hpack_out = libhttppp::hpack::Encoder::encodeResponseHeaders( status_code, content_type, body.size(), extra); // Initialise per-stream peer window for flow control cureq.h2state().peerStreamWindows[sid] = cureq.h2state().peerInitialStreamWindow; // Per-stream peer window was already seeded in _dispatchH2Stream — // see the comment there. Don't overwrite it here; it may already // carry credit from a WINDOW_UPDATE that arrived while an offloaded // render was in flight. cureq.h2state().peerStreamWindows.try_emplace( sid, cureq.h2state().peerInitialStreamWindow); // HEADERS frame uint8_t hdr_flags = H2_FLAG_END_HEADERS; Loading Loading @@ -689,6 +711,11 @@ void libhttppp::HttpEvent::_finishH2Dispatch(HttpRequest &cureq, uint8_t rst[4] = {0, 0, 0, 0x02}; // INTERNAL_ERROR out += h2BuildFrame(H2_FRAME_RST_STREAM, 0, sid, std::string(reinterpret_cast<char*>(rst), 4)); // This stream's window (seeded in _dispatchH2Stream) never got // used since no response body was ever sent — drop it rather than // leaking one stale entry per failed dispatch on a long-lived // connection. cureq.h2state().peerStreamWindows.erase(sid); } } Loading