Loading src/hpack.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -418,13 +418,13 @@ std::string Encoder::encodeResponseHeaders(uint16_t status_code, std::string out; if (status_code == 200) { out.push_back(static_cast<char>(0x88)); out.push_back(static_cast<char>(0x88)); // index 8 } else if (status_code == 204) { out.push_back(static_cast<char>(0x89)); out.push_back(static_cast<char>(0x89)); // index 9 } else if (status_code == 206) { out.push_back(static_cast<char>(0x8b)); out.push_back(static_cast<char>(0x8a)); // index 10 } else if (status_code == 304) { out.push_back(static_cast<char>(0x8f)); out.push_back(static_cast<char>(0x8b)); // index 11 } else { std::string status = std::to_string(status_code); appendLiteralWithNameIndex(out, 0x08, status, false); Loading src/http.cpp +73 −0 Original line number Diff line number Diff line Loading @@ -410,6 +410,18 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b) { } } // Non-blocking recv: returns 0 immediately if no data available (EAGAIN) size_t libhttppp::HttpClient::_recvNonBlocking(netplus::buffer &b) { try { return _cltsock->recvData(b, 0); } catch (netplus::NetException &e) { if (e.getErrorType() == netplus::NetException::Note) { return 0; // EAGAIN — no data yet } throw; } } // Efficient send: uses socketwait::waitWrite() instead of busy-waiting void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { size_t off = 0; Loading Loading @@ -1479,6 +1491,67 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { return 0; } size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize) { // Stream finished: signal completion with (size_t)-1 if (_streamMode == STREAM_NONE || bufsize == 0) return static_cast<size_t>(-1); // Helper: drain from internal leftover buffer auto drainBuf = [&](size_t maxBytes) -> size_t { size_t have = _streamBuf.size() - _streamBufPos; if (have == 0) return 0; size_t take = (std::min)(have, maxBytes); std::memcpy(buf, _streamBuf.data() + _streamBufPos, take); _streamBufPos += take; if (_streamBufPos == _streamBuf.size()) { _streamBuf.clear(); _streamBufPos = 0; } return take; }; // ── Content-Length mode ── if (_streamMode == STREAM_CONTENT_LENGTH) { if (_streamRemaining == 0) { _streamMode = STREAM_NONE; return static_cast<size_t>(-1); } size_t want = (std::min)(bufsize, _streamRemaining); // First try leftover buffer size_t got = drainBuf(want); if (got > 0) { _streamRemaining -= got; if (_streamRemaining == 0) _streamMode = STREAM_NONE; return got; } // Non-blocking read from socket netplus::buffer nb(BLOCKSIZE); size_t n = _recvNonBlocking(nb); if (n == 0) return 0; // No data yet — caller should retry later size_t take = (std::min)({n, want, bufsize}); std::memcpy(buf, nb.data.buf, take); _streamRemaining -= take; if (take < n) { _streamBuf.assign(nb.data.buf + take, nb.data.buf + n); _streamBufPos = 0; } if (_streamRemaining == 0) _streamMode = STREAM_NONE; return take; } // For chunked/EOF/H2/H3 modes, fall back to blocking readBodyChunk return readBodyChunk(buf, bufsize); } static bool is_redirect_status(int code) { return code == 301 || code == 302 || code == 303 || code == 307 || code == 308; } Loading src/http.h +6 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,10 @@ namespace libhttppp { // Returns number of bytes written to buf, 0 when body is complete. size_t readBodyChunk(char *buf, size_t bufsize); // Non-blocking variant: returns 0 immediately if no data available yet. // Returns (size_t)-1 when stream is complete (no more data will come). size_t readBodyChunkNonBlocking(char *buf, size_t bufsize); // True while a streaming read is in progress. bool isStreaming() const; Loading @@ -116,6 +120,8 @@ namespace libhttppp { // Non-blocking I/O helpers using poll() for efficient waiting size_t _recvBlocking(netplus::buffer &b); // Returns 0 on EAGAIN (no data yet), otherwise bytes read size_t _recvNonBlocking(netplus::buffer &b); void _sendAll(const char *data, size_t len); void _sendAll(const std::string &data); Loading src/httpd.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ #include <iostream> #include <cctype> #include <string> #include <thread> #include <chrono> #include <netplus/socket.h> #include <netplus/exception.h> Loading Loading @@ -417,6 +419,7 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, tempreq.SendData.pos = 0; size_t max_iterations = content_length / BLOCKSIZE + 4096; size_t empty_count = 0; for (size_t i = 0; i < max_iterations && body.size() < content_length; ++i) { Loading @@ -425,8 +428,14 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, if (sa > 0) { body.append(tempreq.SendData.data(), sa); tempreq.SendData.clear(); empty_count = 0; } else { break; // No more data available ++empty_count; if (empty_count > 500) { break; // Genuine stall — give up } // No data yet — yield briefly so backend can deliver std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } } Loading Loading
src/hpack.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -418,13 +418,13 @@ std::string Encoder::encodeResponseHeaders(uint16_t status_code, std::string out; if (status_code == 200) { out.push_back(static_cast<char>(0x88)); out.push_back(static_cast<char>(0x88)); // index 8 } else if (status_code == 204) { out.push_back(static_cast<char>(0x89)); out.push_back(static_cast<char>(0x89)); // index 9 } else if (status_code == 206) { out.push_back(static_cast<char>(0x8b)); out.push_back(static_cast<char>(0x8a)); // index 10 } else if (status_code == 304) { out.push_back(static_cast<char>(0x8f)); out.push_back(static_cast<char>(0x8b)); // index 11 } else { std::string status = std::to_string(status_code); appendLiteralWithNameIndex(out, 0x08, status, false); Loading
src/http.cpp +73 −0 Original line number Diff line number Diff line Loading @@ -410,6 +410,18 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b) { } } // Non-blocking recv: returns 0 immediately if no data available (EAGAIN) size_t libhttppp::HttpClient::_recvNonBlocking(netplus::buffer &b) { try { return _cltsock->recvData(b, 0); } catch (netplus::NetException &e) { if (e.getErrorType() == netplus::NetException::Note) { return 0; // EAGAIN — no data yet } throw; } } // Efficient send: uses socketwait::waitWrite() instead of busy-waiting void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { size_t off = 0; Loading Loading @@ -1479,6 +1491,67 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { return 0; } size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize) { // Stream finished: signal completion with (size_t)-1 if (_streamMode == STREAM_NONE || bufsize == 0) return static_cast<size_t>(-1); // Helper: drain from internal leftover buffer auto drainBuf = [&](size_t maxBytes) -> size_t { size_t have = _streamBuf.size() - _streamBufPos; if (have == 0) return 0; size_t take = (std::min)(have, maxBytes); std::memcpy(buf, _streamBuf.data() + _streamBufPos, take); _streamBufPos += take; if (_streamBufPos == _streamBuf.size()) { _streamBuf.clear(); _streamBufPos = 0; } return take; }; // ── Content-Length mode ── if (_streamMode == STREAM_CONTENT_LENGTH) { if (_streamRemaining == 0) { _streamMode = STREAM_NONE; return static_cast<size_t>(-1); } size_t want = (std::min)(bufsize, _streamRemaining); // First try leftover buffer size_t got = drainBuf(want); if (got > 0) { _streamRemaining -= got; if (_streamRemaining == 0) _streamMode = STREAM_NONE; return got; } // Non-blocking read from socket netplus::buffer nb(BLOCKSIZE); size_t n = _recvNonBlocking(nb); if (n == 0) return 0; // No data yet — caller should retry later size_t take = (std::min)({n, want, bufsize}); std::memcpy(buf, nb.data.buf, take); _streamRemaining -= take; if (take < n) { _streamBuf.assign(nb.data.buf + take, nb.data.buf + n); _streamBufPos = 0; } if (_streamRemaining == 0) _streamMode = STREAM_NONE; return take; } // For chunked/EOF/H2/H3 modes, fall back to blocking readBodyChunk return readBodyChunk(buf, bufsize); } static bool is_redirect_status(int code) { return code == 301 || code == 302 || code == 303 || code == 307 || code == 308; } Loading
src/http.h +6 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,10 @@ namespace libhttppp { // Returns number of bytes written to buf, 0 when body is complete. size_t readBodyChunk(char *buf, size_t bufsize); // Non-blocking variant: returns 0 immediately if no data available yet. // Returns (size_t)-1 when stream is complete (no more data will come). size_t readBodyChunkNonBlocking(char *buf, size_t bufsize); // True while a streaming read is in progress. bool isStreaming() const; Loading @@ -116,6 +120,8 @@ namespace libhttppp { // Non-blocking I/O helpers using poll() for efficient waiting size_t _recvBlocking(netplus::buffer &b); // Returns 0 on EAGAIN (no data yet), otherwise bytes read size_t _recvNonBlocking(netplus::buffer &b); void _sendAll(const char *data, size_t len); void _sendAll(const std::string &data); Loading
src/httpd.cpp +10 −1 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ #include <iostream> #include <cctype> #include <string> #include <thread> #include <chrono> #include <netplus/socket.h> #include <netplus/exception.h> Loading Loading @@ -417,6 +419,7 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, tempreq.SendData.pos = 0; size_t max_iterations = content_length / BLOCKSIZE + 4096; size_t empty_count = 0; for (size_t i = 0; i < max_iterations && body.size() < content_length; ++i) { Loading @@ -425,8 +428,14 @@ void libhttppp::HttpEvent::_dispatchH2Stream(HttpRequest &cureq, if (sa > 0) { body.append(tempreq.SendData.data(), sa); tempreq.SendData.clear(); empty_count = 0; } else { break; // No more data available ++empty_count; if (empty_count > 500) { break; // Genuine stall — give up } // No data yet — yield briefly so backend can deliver std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } } Loading