Loading src/http.cpp +104 −70 Original line number Diff line number Diff line Loading @@ -471,7 +471,7 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b, int timeout_sec) ee[HTTPException::Error] << "recv timeout: connection may be dead"; throw ee; } _sw.waitRead(*_cltsock, 5000); _sw.waitRead(*_cltsock, 500); continue; } throw; Loading Loading @@ -506,7 +506,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { ee[HTTPException::Error] << "send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } off += n; Loading @@ -517,7 +517,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { ee[HTTPException::Error] << "send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } throw; Loading @@ -539,10 +539,12 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe raw.reserve(16384); size_t header_end = std::string::npos; size_t search_from = 0; // avoid re-scanning from 0 on every recv (O(n²) → O(n)) for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { size_t start = (search_from >= 3) ? search_from - 3 : 0; for (size_t i = start; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { Loading @@ -550,6 +552,7 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe break; } } search_from = raw.size(); } if (header_end != std::string::npos) break; Loading Loading @@ -1001,7 +1004,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest _streamH2EndStream = false; auto h2_ensure_bytes = [&](size_t need) { while (_streamH2Raw.size() < need) { while (_streamH2Raw.size() - h2_off < need) { netplus::buffer buf(CHUNKSIZE); size_t n = _recvBlocking(buf, 15); if (n == 0) { Loading @@ -1015,22 +1018,24 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest } }; size_t h2_off = 0; while (!got_headers) { h2_ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading Loading @@ -1068,8 +1073,9 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest // Body data arrived with headers — save for readBodyChunk _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(stream_id, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(stream_id, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading @@ -1091,8 +1097,13 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } _streamMode = STREAM_H2; Loading @@ -1117,10 +1128,12 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest std::vector<char> raw; raw.reserve(16384); size_t header_end = std::string::npos; size_t search_from = 0; for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { size_t start = (search_from >= 3) ? search_from - 3 : 0; for (size_t i = start; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { Loading @@ -1128,6 +1141,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest break; } } search_from = raw.size(); } if (header_end != std::string::npos) break; Loading Loading @@ -1234,7 +1248,9 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { netplus::buffer nb(CHUNKSIZE); size_t n = _recvBlocking(nb); if (n > 0) { if (_streamBufPos > 0 && _streamBufPos < _streamBuf.size()) { // Compact only when consumed portion exceeds half the buffer // to amortize the O(n) memmove cost if (_streamBufPos > 0 && _streamBufPos >= _streamBuf.size() / 2) { _streamBuf.erase(_streamBuf.begin(), _streamBuf.begin() + (ptrdiff_t)_streamBufPos); _streamBufPos = 0; Loading Loading @@ -1430,8 +1446,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { } // Read more H2 frames until we get DATA for our stream size_t h2_off = 0; // track consumed position instead of erasing per frame auto h2_ensure_bytes = [&](size_t need) { while (_streamH2Raw.size() < need) { while (_streamH2Raw.size() - h2_off < need) { netplus::buffer fbuf(CHUNKSIZE); size_t n = _recvBlocking(fbuf); if (n == 0) { Loading @@ -1448,20 +1466,20 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { h2_ensure_bytes(H2C_FRAME_HEADER_LEN); if (_streamH2EndStream) break; uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); if (_streamH2EndStream) break; const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading @@ -1479,8 +1497,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { } _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len)); // Batch both WINDOW_UPDATE frames into a single send std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(_streamH2Sid, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading @@ -1507,8 +1527,13 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } // Drain any newly received body data Loading Loading @@ -1878,21 +1903,22 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize } // Process all complete H2 frames in _streamH2Raw while (_streamH2Raw.size() >= H2C_FRAME_HEADER_LEN) { uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); if (_streamH2Raw.size() < H2C_FRAME_HEADER_LEN + frame_len) size_t h2_off = 0; while (_streamH2Raw.size() - h2_off >= H2C_FRAME_HEADER_LEN) { uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); if (_streamH2Raw.size() - h2_off < H2C_FRAME_HEADER_LEN + frame_len) break; // incomplete frame, wait for more data const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading @@ -1909,8 +1935,9 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize } _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(_streamH2Sid, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading Loading @@ -1939,8 +1966,13 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } // Drain newly received body data Loading Loading @@ -2152,7 +2184,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( ee[HTTPException::Error] << "H2 send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } off += n; Loading @@ -2163,7 +2195,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( ee[HTTPException::Error] << "H2 send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } throw; Loading Loading @@ -2361,6 +2393,8 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( // malicious server sends endless non-terminal frames (e.g. PINGs). auto h2_response_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(120); size_t raw_off = 0; // track consumed position to avoid per-frame erase while (!got_end_stream) { if (std::chrono::steady_clock::now() >= h2_response_deadline) { HTTPException ee; Loading @@ -2368,22 +2402,22 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( throw ee; } // Read frame header (9 bytes) ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[0]) << 16) | (static_cast<uint32_t>(raw[1]) << 8) | static_cast<uint32_t>(raw[2]); uint8_t frame_type = raw[3]; uint8_t frame_flags = raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[6]) << 16) | (static_cast<uint32_t>(raw[7]) << 8) | static_cast<uint32_t>(raw[8]); ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[raw_off]) << 16) | (static_cast<uint32_t>(raw[raw_off+1]) << 8) | static_cast<uint32_t>(raw[raw_off+2]); uint8_t frame_type = raw[raw_off+3]; uint8_t frame_flags = raw[raw_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[raw_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[raw_off+6]) << 16) | (static_cast<uint32_t>(raw[raw_off+7]) << 8) | static_cast<uint32_t>(raw[raw_off+8]); // Read frame payload ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = raw.data() + raw_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading Loading @@ -2413,10 +2447,11 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } // Send WINDOW_UPDATE for connection and stream // Send WINDOW_UPDATE for connection and stream (batched) if (frame_len > 0) { send_all(h2cBuildWindowUpdate(0, frame_len)); send_all(h2cBuildWindowUpdate(stream_id, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(stream_id, frame_len); send_all(wu); } } break; Loading @@ -2442,8 +2477,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( break; } // Consume processed frame from buffer raw.erase(raw.begin(), raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); raw_off += H2C_FRAME_HEADER_LEN + frame_len; } return ret; Loading Loading
src/http.cpp +104 −70 Original line number Diff line number Diff line Loading @@ -471,7 +471,7 @@ size_t libhttppp::HttpClient::_recvBlocking(netplus::buffer &b, int timeout_sec) ee[HTTPException::Error] << "recv timeout: connection may be dead"; throw ee; } _sw.waitRead(*_cltsock, 5000); _sw.waitRead(*_cltsock, 500); continue; } throw; Loading Loading @@ -506,7 +506,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { ee[HTTPException::Error] << "send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } off += n; Loading @@ -517,7 +517,7 @@ void libhttppp::HttpClient::_sendAll(const char *data, size_t len) { ee[HTTPException::Error] << "send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } throw; Loading @@ -539,10 +539,12 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe raw.reserve(16384); size_t header_end = std::string::npos; size_t search_from = 0; // avoid re-scanning from 0 on every recv (O(n²) → O(n)) for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { size_t start = (search_from >= 3) ? search_from - 3 : 0; for (size_t i = start; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { Loading @@ -550,6 +552,7 @@ std::vector<char> libhttppp::HttpClient::_h1ReadResponse(const std::string &labe break; } } search_from = raw.size(); } if (header_end != std::string::npos) break; Loading Loading @@ -1001,7 +1004,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest _streamH2EndStream = false; auto h2_ensure_bytes = [&](size_t need) { while (_streamH2Raw.size() < need) { while (_streamH2Raw.size() - h2_off < need) { netplus::buffer buf(CHUNKSIZE); size_t n = _recvBlocking(buf, 15); if (n == 0) { Loading @@ -1015,22 +1018,24 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest } }; size_t h2_off = 0; while (!got_headers) { h2_ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading Loading @@ -1068,8 +1073,9 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest // Body data arrived with headers — save for readBodyChunk _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(stream_id, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(stream_id, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading @@ -1091,8 +1097,13 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } _streamMode = STREAM_H2; Loading @@ -1117,10 +1128,12 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest std::vector<char> raw; raw.reserve(16384); size_t header_end = std::string::npos; size_t search_from = 0; for (;;) { if (raw.size() >= 4) { for (size_t i = 0; i + 3 < raw.size(); ++i) { size_t start = (search_from >= 3) ? search_from - 3 : 0; for (size_t i = start; i + 3 < raw.size(); ++i) { if (raw[i] == '\r' && raw[i+1] == '\n' && raw[i+2] == '\r' && raw[i+3] == '\n') { Loading @@ -1128,6 +1141,7 @@ libhttppp::HttpResponse libhttppp::HttpClient::GetStream(libhttppp::HttpRequest break; } } search_from = raw.size(); } if (header_end != std::string::npos) break; Loading Loading @@ -1234,7 +1248,9 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { netplus::buffer nb(CHUNKSIZE); size_t n = _recvBlocking(nb); if (n > 0) { if (_streamBufPos > 0 && _streamBufPos < _streamBuf.size()) { // Compact only when consumed portion exceeds half the buffer // to amortize the O(n) memmove cost if (_streamBufPos > 0 && _streamBufPos >= _streamBuf.size() / 2) { _streamBuf.erase(_streamBuf.begin(), _streamBuf.begin() + (ptrdiff_t)_streamBufPos); _streamBufPos = 0; Loading Loading @@ -1430,8 +1446,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { } // Read more H2 frames until we get DATA for our stream size_t h2_off = 0; // track consumed position instead of erasing per frame auto h2_ensure_bytes = [&](size_t need) { while (_streamH2Raw.size() < need) { while (_streamH2Raw.size() - h2_off < need) { netplus::buffer fbuf(CHUNKSIZE); size_t n = _recvBlocking(fbuf); if (n == 0) { Loading @@ -1448,20 +1466,20 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { h2_ensure_bytes(H2C_FRAME_HEADER_LEN); if (_streamH2EndStream) break; uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); h2_ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); if (_streamH2EndStream) break; const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading @@ -1479,8 +1497,10 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { } _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len)); // Batch both WINDOW_UPDATE frames into a single send std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(_streamH2Sid, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading @@ -1507,8 +1527,13 @@ size_t libhttppp::HttpClient::readBodyChunk(char *buf, size_t bufsize) { break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } // Drain any newly received body data Loading Loading @@ -1878,21 +1903,22 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize } // Process all complete H2 frames in _streamH2Raw while (_streamH2Raw.size() >= H2C_FRAME_HEADER_LEN) { uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[0]) << 16) | (static_cast<uint32_t>(_streamH2Raw[1]) << 8) | static_cast<uint32_t>(_streamH2Raw[2]); uint8_t frame_type = _streamH2Raw[3]; uint8_t frame_flags = _streamH2Raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[7]) << 8) | static_cast<uint32_t>(_streamH2Raw[8]); if (_streamH2Raw.size() < H2C_FRAME_HEADER_LEN + frame_len) size_t h2_off = 0; while (_streamH2Raw.size() - h2_off >= H2C_FRAME_HEADER_LEN) { uint32_t frame_len = (static_cast<uint32_t>(_streamH2Raw[h2_off]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+1]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+2]); uint8_t frame_type = _streamH2Raw[h2_off+3]; uint8_t frame_flags = _streamH2Raw[h2_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(_streamH2Raw[h2_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(_streamH2Raw[h2_off+6]) << 16) | (static_cast<uint32_t>(_streamH2Raw[h2_off+7]) << 8) | static_cast<uint32_t>(_streamH2Raw[h2_off+8]); if (_streamH2Raw.size() - h2_off < H2C_FRAME_HEADER_LEN + frame_len) break; // incomplete frame, wait for more data const uint8_t *payload = _streamH2Raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = _streamH2Raw.data() + h2_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading @@ -1909,8 +1935,9 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize } _streamBuf.insert(_streamBuf.end(), payload, payload + frame_len); if (frame_len > 0) { _sendAll(h2cBuildWindowUpdate(0, frame_len)); _sendAll(h2cBuildWindowUpdate(_streamH2Sid, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(_streamH2Sid, frame_len); _sendAll(wu); } if (frame_flags & H2C_FLAG_END_STREAM) _streamH2EndStream = true; Loading Loading @@ -1939,8 +1966,13 @@ size_t libhttppp::HttpClient::readBodyChunkNonBlocking(char *buf, size_t bufsize break; } h2_off += H2C_FRAME_HEADER_LEN + frame_len; } // Single compact: remove all consumed bytes at once if (h2_off > 0) { _streamH2Raw.erase(_streamH2Raw.begin(), _streamH2Raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); _streamH2Raw.begin() + static_cast<ptrdiff_t>(h2_off)); } // Drain newly received body data Loading Loading @@ -2152,7 +2184,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( ee[HTTPException::Error] << "H2 send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } off += n; Loading @@ -2163,7 +2195,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( ee[HTTPException::Error] << "H2 send timeout: connection may be dead"; throw ee; } _sw.waitWrite(*_cltsock, 5000); _sw.waitWrite(*_cltsock, 500); continue; } throw; Loading Loading @@ -2361,6 +2393,8 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( // malicious server sends endless non-terminal frames (e.g. PINGs). auto h2_response_deadline = std::chrono::steady_clock::now() + std::chrono::seconds(120); size_t raw_off = 0; // track consumed position to avoid per-frame erase while (!got_end_stream) { if (std::chrono::steady_clock::now() >= h2_response_deadline) { HTTPException ee; Loading @@ -2368,22 +2402,22 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( throw ee; } // Read frame header (9 bytes) ensure_bytes(H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[0]) << 16) | (static_cast<uint32_t>(raw[1]) << 8) | static_cast<uint32_t>(raw[2]); uint8_t frame_type = raw[3]; uint8_t frame_flags = raw[4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[6]) << 16) | (static_cast<uint32_t>(raw[7]) << 8) | static_cast<uint32_t>(raw[8]); ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN); uint32_t frame_len = (static_cast<uint32_t>(raw[raw_off]) << 16) | (static_cast<uint32_t>(raw[raw_off+1]) << 8) | static_cast<uint32_t>(raw[raw_off+2]); uint8_t frame_type = raw[raw_off+3]; uint8_t frame_flags = raw[raw_off+4]; uint32_t frame_stream = ((static_cast<uint32_t>(raw[raw_off+5]) & 0x7f) << 24) | (static_cast<uint32_t>(raw[raw_off+6]) << 16) | (static_cast<uint32_t>(raw[raw_off+7]) << 8) | static_cast<uint32_t>(raw[raw_off+8]); // Read frame payload ensure_bytes(H2C_FRAME_HEADER_LEN + frame_len); ensure_bytes(raw_off + H2C_FRAME_HEADER_LEN + frame_len); const uint8_t *payload = raw.data() + H2C_FRAME_HEADER_LEN; const uint8_t *payload = raw.data() + raw_off + H2C_FRAME_HEADER_LEN; switch (frame_type) { case H2C_FRAME_SETTINGS: { Loading Loading @@ -2413,10 +2447,11 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( if (frame_flags & H2C_FLAG_END_STREAM) { got_end_stream = true; } // Send WINDOW_UPDATE for connection and stream // Send WINDOW_UPDATE for connection and stream (batched) if (frame_len > 0) { send_all(h2cBuildWindowUpdate(0, frame_len)); send_all(h2cBuildWindowUpdate(stream_id, frame_len)); std::string wu = h2cBuildWindowUpdate(0, frame_len) + h2cBuildWindowUpdate(stream_id, frame_len); send_all(wu); } } break; Loading @@ -2442,8 +2477,7 @@ const std::vector<char> libhttppp::HttpClient::_h2Request( break; } // Consume processed frame from buffer raw.erase(raw.begin(), raw.begin() + H2C_FRAME_HEADER_LEN + frame_len); raw_off += H2C_FRAME_HEADER_LEN + frame_len; } return ret; Loading