Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -7133,7 +7133,7 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
}

inline ssize_t SocketStream::write(const char *ptr, size_t size) {
if (!wait_writable()) { return -1; }
if (!detail::is_socket_alive(sock_) || !wait_writable()) { return -1; }

#if defined(_WIN32) && !defined(_WIN64)
size =
Expand Down Expand Up @@ -8618,7 +8618,11 @@ inline ClientImpl::ClientImpl(const std::string &host, int port,
const std::string &client_key_path)
: host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
host_and_port_(detail::make_host_and_port_string(host_, port, is_ssl())),
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A C/C++ library shall not call signal.

Copy link
Contributor Author

@chansikpark chansikpark Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd been wondering why eg the README has this note:

If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.

While the server code consumes SIGPIPE.. :

cpp-httplib/httplib.h

Lines 7322 to 7328 in 4b2b851

inline Server::Server()
: new_task_queue(
[] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
}

In any case, I'd concede that the other two changes probably make the client-side signal unnecessary for what I was trying to do. Been meaning to play around a little more to better understand what I'm doing. Will revert particular change in the meantime.

#endif
}

inline ClientImpl::~ClientImpl() {
// Wait until all the requests in flight are handled.
Expand Down Expand Up @@ -9516,7 +9520,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
Response &res, bool close_connection,
Error &error) {
// Send request
if (!write_request(strm, req, close_connection, error)) { return false; }
bool wrote = write_request(strm, req, close_connection, error);

#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
if (is_ssl()) {
Expand All @@ -9539,6 +9543,8 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
return false;
}

if (!wrote) return false;

// Body
if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
req.method != "CONNECT") {
Expand Down
43 changes: 43 additions & 0 deletions test/test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5898,6 +5898,49 @@ TEST_F(ServerTest, BadRequestLineCancelsKeepAlive) {
EXPECT_FALSE(cli_.is_socket_open());
}

TEST_F(ServerTest, SendLargeBodyAfterRequestLineError) {
Request post;
post.method = "POST";
post.path = "/post-large?q=" + LONG_QUERY_VALUE;
post.body = LARGE_DATA;

auto start = std::chrono::high_resolution_clock::now();

auto resPost = std::make_shared<Response>();
auto error = Error::Success;
cli_.set_keep_alive(true);
auto ret = cli_.send(post, *resPost, error);

auto end = std::chrono::high_resolution_clock::now();
auto elapsed =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
.count();

EXPECT_FALSE(ret);
#ifndef _WIN32
EXPECT_EQ(StatusCode::UriTooLong_414, resPost->status);
EXPECT_EQ("close", resPost->get_header_value("Connection"));
#endif
EXPECT_FALSE(cli_.is_socket_open());
EXPECT_LE(elapsed, 200);

// Send an extra GET request to ensure error recovery without hanging
Request get;
get.method = "GET";
get.path = "/hi";

start = std::chrono::high_resolution_clock::now();
auto resGet = cli_.send(get);
end = std::chrono::high_resolution_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
.count();

ASSERT_TRUE(resGet);
EXPECT_EQ(StatusCode::OK_200, resGet->status);
EXPECT_EQ("Hello World!", resGet->body);
EXPECT_LE(elapsed, 100);
}

TEST_F(ServerTest, StartTime) { auto res = cli_.Get("/test-start-time"); }

#ifdef CPPHTTPLIB_ZLIB_SUPPORT
Expand Down