Loading plugins/media/media.cpp +54 −2 Original line number Diff line number Diff line Loading @@ -1129,9 +1129,7 @@ namespace blogi { return true; } // Store streaming state per-request req.RecvData.pos = 0; _storeStreamState(req, std::move(streamClient), sendSize, tid); libhttppp::HttpResponse curres; // Set content type based on requested format Loading Loading @@ -1161,6 +1159,60 @@ namespace blogi { } curres.setContentLength(sendSize); // HTTP/1.x has no equivalent of H2's _resumeH2Streams (the // WINDOW_UPDATE-driven self-resumption libhttppp uses to keep // calling ResponseEvent for a deferred/streaming response) -- // see proxyplus's HttpProxyServer.cpp Range branch for the // same distinction. Deferring via _storeStreamState() here and // returning would leave the stream permanently un-drained on // H1: nothing ever calls Response() again, and it just sits // until the STREAM_IDLE_TIMEOUT reaper discards it 30s later // with zero bytes sent (confirmed live via [STREAM-DIAG]). // H1 offload already runs this Controller() call on its own // dedicated blocking worker thread (see libhttppp's // _dispatchH1Request), so draining the whole body // synchronously right here is exactly as safe as proxyplus's // identical H1 fallback. bool isH1 = req.getRequestVersion().rfind("HTTP/1", 0) == 0; if (isH1) { curres.send(req, "", -1); if (!req.flushSendData()) { streamClient.discard(); return true; } char chunk[65536]; size_t produced = 0; bool deliveryOk = true; while (produced < sendSize) { const size_t remaining = sendSize - produced; const size_t toRead = remaining < sizeof(chunk) ? remaining : sizeof(chunk); size_t n; try { n = streamClient->readBodyChunk(chunk, toRead); } catch (std::exception &e) { std::cerr << "media: H1 stream failed after " << produced << "/" << sendSize << " bytes: " << e.what() << std::endl; deliveryOk = false; break; } if (n == 0) break; req.SendData.append(chunk, n); produced += n; if (!req.flushSendData()) { deliveryOk = false; break; } } // Only let the connection go back to the pool if the body was fully // drained -- an early break (upstream error, client gave up) leaves this // HttpClient's read state unknown, same reasoning as every other // discard() call site in this file. if (!deliveryOk || produced < sendSize) { streamClient.discard(); } return true; } // Store streaming state per-request _storeStreamState(req, std::move(streamClient), sendSize, tid); curres.send(req, "", -1); return true; } Loading Loading
plugins/media/media.cpp +54 −2 Original line number Diff line number Diff line Loading @@ -1129,9 +1129,7 @@ namespace blogi { return true; } // Store streaming state per-request req.RecvData.pos = 0; _storeStreamState(req, std::move(streamClient), sendSize, tid); libhttppp::HttpResponse curres; // Set content type based on requested format Loading Loading @@ -1161,6 +1159,60 @@ namespace blogi { } curres.setContentLength(sendSize); // HTTP/1.x has no equivalent of H2's _resumeH2Streams (the // WINDOW_UPDATE-driven self-resumption libhttppp uses to keep // calling ResponseEvent for a deferred/streaming response) -- // see proxyplus's HttpProxyServer.cpp Range branch for the // same distinction. Deferring via _storeStreamState() here and // returning would leave the stream permanently un-drained on // H1: nothing ever calls Response() again, and it just sits // until the STREAM_IDLE_TIMEOUT reaper discards it 30s later // with zero bytes sent (confirmed live via [STREAM-DIAG]). // H1 offload already runs this Controller() call on its own // dedicated blocking worker thread (see libhttppp's // _dispatchH1Request), so draining the whole body // synchronously right here is exactly as safe as proxyplus's // identical H1 fallback. bool isH1 = req.getRequestVersion().rfind("HTTP/1", 0) == 0; if (isH1) { curres.send(req, "", -1); if (!req.flushSendData()) { streamClient.discard(); return true; } char chunk[65536]; size_t produced = 0; bool deliveryOk = true; while (produced < sendSize) { const size_t remaining = sendSize - produced; const size_t toRead = remaining < sizeof(chunk) ? remaining : sizeof(chunk); size_t n; try { n = streamClient->readBodyChunk(chunk, toRead); } catch (std::exception &e) { std::cerr << "media: H1 stream failed after " << produced << "/" << sendSize << " bytes: " << e.what() << std::endl; deliveryOk = false; break; } if (n == 0) break; req.SendData.append(chunk, n); produced += n; if (!req.flushSendData()) { deliveryOk = false; break; } } // Only let the connection go back to the pool if the body was fully // drained -- an early break (upstream error, client gave up) leaves this // HttpClient's read state unknown, same reasoning as every other // discard() call site in this file. if (!deliveryOk || produced < sendSize) { streamClient.discard(); } return true; } // Store streaming state per-request _storeStreamState(req, std::move(streamClient), sendSize, tid); curres.send(req, "", -1); return true; } Loading