Skip to content

Commit 12dc73e

Browse files
committed
Add transfer_socket_ownership_to_handle helper and parity test for Get vs open_stream
1 parent 5667366 commit 12dc73e

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

httplib.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,6 +1922,12 @@ class ClientImpl {
19221922
std::chrono::time_point<std::chrono::steady_clock> start_time,
19231923
std::function<bool(Stream &strm)> callback);
19241924
virtual bool is_ssl() const;
1925+
1926+
// Helper: transfer the current `socket_` ownership into a StreamHandle's
1927+
// ClientConnection. This moves `socket_.sock`/`socket_.ssl` into
1928+
// `handle.connection_` and resets `socket_` to closed. This method MUST be
1929+
// called with `socket_mutex_` held.
1930+
void transfer_socket_ownership_to_handle(StreamHandle &handle);
19251931
};
19261932

19271933
class Client {
@@ -9716,13 +9722,8 @@ ClientImpl::open_stream(const std::string &method, const std::string &path,
97169722
#endif
97179723
}
97189724

9719-
// Transfer socket ownership to StreamHandle
9720-
handle.connection_->sock = socket_.sock;
9721-
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9722-
handle.connection_->ssl = socket_.ssl;
9723-
socket_.ssl = nullptr;
9724-
#endif
9725-
socket_.sock = INVALID_SOCKET;
9725+
// Transfer socket ownership to StreamHandle (centralized helper)
9726+
transfer_socket_ownership_to_handle(handle);
97269727
}
97279728

97289729
// Create appropriate stream for the transferred socket
@@ -9808,6 +9809,17 @@ ClientImpl::open_stream(const std::string &method, const std::string &path,
98089809
return handle;
98099810
}
98109811

9812+
inline void
9813+
ClientImpl::transfer_socket_ownership_to_handle(StreamHandle &handle) {
9814+
// Caller must hold socket_mutex_. Move socket_ contents into handle.
9815+
handle.connection_->sock = socket_.sock;
9816+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9817+
handle.connection_->ssl = socket_.ssl;
9818+
socket_.ssl = nullptr;
9819+
#endif
9820+
socket_.sock = INVALID_SOCKET;
9821+
}
9822+
98119823
inline bool ClientImpl::handle_request(Stream &strm, Request &req,
98129824
Response &res, bool close_connection,
98139825
Error &error) {

test/test.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,48 @@ TEST(BenchmarkTest, localhost) { performance_test("localhost"); }
146146

147147
TEST(BenchmarkTest, v6) { performance_test("::1"); }
148148

149+
TEST(ParityTest, GetVsOpenStream) {
150+
Server svr;
151+
152+
const std::string path = "/parity";
153+
const std::string content = "Parity test content: hello world";
154+
155+
svr.Get(path, [&](const Request & /*req*/, Response &res) {
156+
res.set_content(content, "text/plain");
157+
});
158+
159+
auto listen_thread = std::thread([&]() { svr.listen(HOST, PORT); });
160+
auto se = detail::scope_exit([&] {
161+
svr.stop();
162+
listen_thread.join();
163+
ASSERT_FALSE(svr.is_running());
164+
});
165+
166+
svr.wait_until_ready();
167+
168+
Client cli(HOST, PORT);
169+
170+
// Non-stream path
171+
auto r1 = cli.Get(path);
172+
ASSERT_TRUE(r1);
173+
EXPECT_EQ(StatusCode::OK_200, r1->status);
174+
std::string body1 = r1->body;
175+
176+
// Stream path
177+
auto h = cli.open_stream("GET", path);
178+
ASSERT_TRUE(h.is_valid());
179+
std::string body2;
180+
char buf[128];
181+
for (;;) {
182+
auto n = h.read(buf, sizeof(buf));
183+
if (n < 0) { break; }
184+
if (n == 0) { break; }
185+
body2.append(buf, static_cast<size_t>(n));
186+
}
187+
188+
EXPECT_EQ(body1, body2);
189+
}
190+
149191
class UnixSocketTest : public ::testing::Test {
150192
protected:
151193
void TearDown() override { std::remove(pathname_.c_str()); }

0 commit comments

Comments
 (0)