Loading src/http.cpp +299 −428 File changed.Preview size limit exceeded, changes collapsed. Show changes src/http.h +16 −0 Changes for src/http.h: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -185,6 +185,22 @@ namespace libhttppp { size_t _streamReadChunked(char *buf, size_t bufsize, bool blocking); size_t _streamReadEof(char *buf, size_t bufsize, bool blocking); // Same idea for STREAM_H2: readBodyChunk() and // readBodyChunkNonBlocking() previously carried two independently // hand-copied H2 frame-dispatch switches, which had already drifted // (the non-blocking copy had gained an explicit // H2C_FRAME_WINDOW_UPDATE case the blocking one lacked). The frame // dispatch itself is now written once; only how bytes are pulled off // the wire (wait vs. one non-blocking attempt) differs by `blocking`. size_t _streamReadH2(char *buf, size_t bufsize, bool blocking); // Same idea for STREAM_H3: the QUIC varint frame-parsing loop (partial // DATA-frame continuation handling included) was duplicated verbatim // between readBodyChunk() and readBodyChunkNonBlocking(); now written // once and shared, with only the "wait for more data vs. try once and // return" difference kept per-mode. size_t _streamReadH3(char *buf, size_t bufsize, bool blocking); // Shared Get/Post/Put/Delete implementation: builds+sends the request // over whichever transport is active (H1/H2/H3), follows redirects // per RFC 7231 §6.4 (303 always converts to a bodyless GET; 307/308 Loading test/http2_checksum_test.cpp +54 −0 Changes for test/http2_checksum_test.cpp: 54 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -478,6 +478,60 @@ int main() { "HttpClient POST " + label + ": server-reported sha256 matches client's"); } } // ---- Streaming download via GetStream()/readBodyChunk() and // readBodyChunkNonBlocking(), both over the real H2 client path // (_streamReadH2()) -- 1MB guarantees the response spans several // real DATA frames (H2C_MAX_FRAME_SIZE=16384) and at least one // WINDOW_UPDATE round trip, unlike the buffered Get() sweep above // which never touches this code at all. ---- { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = httpClt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; for (;;) { size_t n = httpClt.readBodyChunk(buf, sizeof(buf)); if (n == 0) break; collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "HTTP/2 streaming GET (blocking): 200 response"); check(collected.size() == size, "HTTP/2 streaming GET (blocking): total bytes match"); check(got == want, "HTTP/2 streaming GET (blocking): incrementally-hashed sha256 matches header"); } { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = httpClt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); for (;;) { if (std::chrono::steady_clock::now() > deadline) break; size_t n = httpClt.readBodyChunkNonBlocking(buf, sizeof(buf)); if (n == static_cast<size_t>(-1)) break; if (n == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "HTTP/2 streaming GET (non-blocking): 200 response"); check(collected.size() == size, "HTTP/2 streaming GET (non-blocking): total bytes match"); check(got == want, "HTTP/2 streaming GET (non-blocking): incrementally-hashed sha256 matches header"); } } // shutdownGuard stops and joins the server thread here (its Loading test/http3_checksum_test.cpp +28 −0 Changes for test/http3_checksum_test.cpp: 28 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -183,6 +183,34 @@ int main() { check(got == want, "streaming GET: incrementally-hashed sha256 matches header"); } // ---- Same, via readBodyChunkNonBlocking() (_streamReadH3(blocking=false)) ---- { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = clt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); for (;;) { if (std::chrono::steady_clock::now() > deadline) break; size_t n = clt.readBodyChunkNonBlocking(buf, sizeof(buf)); if (n == static_cast<size_t>(-1)) break; if (n == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "streaming GET (non-blocking): 200 response"); check(collected.size() == size, "streaming GET (non-blocking): total bytes match"); check(got == want, "streaming GET (non-blocking): incrementally-hashed sha256 matches header"); } // shutdownGuard stops and joins the server thread here. } catch (libhttppp::HTTPException &e) { Loading Loading
src/http.h +16 −0 Changes for src/http.h: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -185,6 +185,22 @@ namespace libhttppp { size_t _streamReadChunked(char *buf, size_t bufsize, bool blocking); size_t _streamReadEof(char *buf, size_t bufsize, bool blocking); // Same idea for STREAM_H2: readBodyChunk() and // readBodyChunkNonBlocking() previously carried two independently // hand-copied H2 frame-dispatch switches, which had already drifted // (the non-blocking copy had gained an explicit // H2C_FRAME_WINDOW_UPDATE case the blocking one lacked). The frame // dispatch itself is now written once; only how bytes are pulled off // the wire (wait vs. one non-blocking attempt) differs by `blocking`. size_t _streamReadH2(char *buf, size_t bufsize, bool blocking); // Same idea for STREAM_H3: the QUIC varint frame-parsing loop (partial // DATA-frame continuation handling included) was duplicated verbatim // between readBodyChunk() and readBodyChunkNonBlocking(); now written // once and shared, with only the "wait for more data vs. try once and // return" difference kept per-mode. size_t _streamReadH3(char *buf, size_t bufsize, bool blocking); // Shared Get/Post/Put/Delete implementation: builds+sends the request // over whichever transport is active (H1/H2/H3), follows redirects // per RFC 7231 §6.4 (303 always converts to a bodyless GET; 307/308 Loading
test/http2_checksum_test.cpp +54 −0 Changes for test/http2_checksum_test.cpp: 54 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -478,6 +478,60 @@ int main() { "HttpClient POST " + label + ": server-reported sha256 matches client's"); } } // ---- Streaming download via GetStream()/readBodyChunk() and // readBodyChunkNonBlocking(), both over the real H2 client path // (_streamReadH2()) -- 1MB guarantees the response spans several // real DATA frames (H2C_MAX_FRAME_SIZE=16384) and at least one // WINDOW_UPDATE round trip, unlike the buffered Get() sweep above // which never touches this code at all. ---- { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = httpClt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; for (;;) { size_t n = httpClt.readBodyChunk(buf, sizeof(buf)); if (n == 0) break; collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "HTTP/2 streaming GET (blocking): 200 response"); check(collected.size() == size, "HTTP/2 streaming GET (blocking): total bytes match"); check(got == want, "HTTP/2 streaming GET (blocking): incrementally-hashed sha256 matches header"); } { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = httpClt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); for (;;) { if (std::chrono::steady_clock::now() > deadline) break; size_t n = httpClt.readBodyChunkNonBlocking(buf, sizeof(buf)); if (n == static_cast<size_t>(-1)) break; if (n == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "HTTP/2 streaming GET (non-blocking): 200 response"); check(collected.size() == size, "HTTP/2 streaming GET (non-blocking): total bytes match"); check(got == want, "HTTP/2 streaming GET (non-blocking): incrementally-hashed sha256 matches header"); } } // shutdownGuard stops and joins the server thread here (its Loading
test/http3_checksum_test.cpp +28 −0 Changes for test/http3_checksum_test.cpp: 28 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -183,6 +183,34 @@ int main() { check(got == want, "streaming GET: incrementally-hashed sha256 matches header"); } // ---- Same, via readBodyChunkNonBlocking() (_streamReadH3(blocking=false)) ---- { const size_t size = 1048576; libhttppp::HttpRequest req; req.setRequestURL("/gen/" + std::to_string(size)); libhttppp::HttpResponse res = clt.GetStream(req); std::string want = headerValue(res, "x-sha256"); std::vector<char> collected; collected.reserve(size); char buf[4096]; auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); for (;;) { if (std::chrono::steady_clock::now() > deadline) break; size_t n = clt.readBodyChunkNonBlocking(buf, sizeof(buf)); if (n == static_cast<size_t>(-1)) break; if (n == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } collected.insert(collected.end(), buf, buf + n); } std::string got = sha256Hex(collected); check(res.getStatusCode() == 200, "streaming GET (non-blocking): 200 response"); check(collected.size() == size, "streaming GET (non-blocking): total bytes match"); check(got == want, "streaming GET (non-blocking): incrementally-hashed sha256 matches header"); } // shutdownGuard stops and joins the server thread here. } catch (libhttppp::HTTPException &e) { Loading