Skip to content

Commit 87e03dd

Browse files
authored
Report connection timeout as separate event (#1171)
1 parent e5cacb4 commit 87e03dd

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

httplib.h

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,7 @@ enum class Error {
799799
SSLServerVerification,
800800
UnsupportedMultipartBoundaryChars,
801801
Compression,
802+
ConnectionTimeout,
802803
};
803804

804805
std::string to_string(const Error error);
@@ -1594,6 +1595,7 @@ inline std::string to_string(const Error error) {
15941595
case Error::UnsupportedMultipartBoundaryChars:
15951596
return "UnsupportedMultipartBoundaryChars";
15961597
case Error::Compression: return "Compression";
1598+
case Error::ConnectionTimeout: return "ConnectionTimeout";
15971599
case Error::Unknown: return "Unknown";
15981600
default: break;
15991601
}
@@ -2313,7 +2315,7 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
23132315
#endif
23142316
}
23152317

2316-
inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
2318+
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
23172319
#ifdef CPPHTTPLIB_USE_POLL
23182320
struct pollfd pfd_read;
23192321
pfd_read.fd = sock;
@@ -2323,17 +2325,23 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
23232325

23242326
auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
23252327

2328+
if (poll_res == 0) {
2329+
return Error::ConnectionTimeout;
2330+
}
2331+
23262332
if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
23272333
int error = 0;
23282334
socklen_t len = sizeof(error);
23292335
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
23302336
reinterpret_cast<char *>(&error), &len);
2331-
return res >= 0 && !error;
2337+
auto successful = res >= 0 && !error;
2338+
return successful ? Error::Success : Error::Connection;
23322339
}
2333-
return false;
2340+
2341+
return Error::Connection;
23342342
#else
23352343
#ifndef _WIN32
2336-
if (sock >= FD_SETSIZE) { return false; }
2344+
if (sock >= FD_SETSIZE) { return Error::Connection; }
23372345
#endif
23382346

23392347
fd_set fdsr;
@@ -2351,14 +2359,19 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
23512359
return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
23522360
});
23532361

2362+
if (ret == 0) {
2363+
return Error::ConnectionTimeout;
2364+
}
2365+
23542366
if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
23552367
int error = 0;
23562368
socklen_t len = sizeof(error);
2357-
return getsockopt(sock, SOL_SOCKET, SO_ERROR,
2358-
reinterpret_cast<char *>(&error), &len) >= 0 &&
2359-
!error;
2369+
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
2370+
reinterpret_cast<char *>(&error), &len);
2371+
auto successful = res >= 0 && !error;
2372+
return successful ? Error::Success : Error::Connection;
23602373
}
2361-
return false;
2374+
return Error::Connection;
23622375
#endif
23632376
}
23642377

@@ -2684,12 +2697,15 @@ inline socket_t create_client_socket(
26842697
::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
26852698

26862699
if (ret < 0) {
2687-
if (is_connection_error() ||
2688-
!wait_until_socket_is_ready(sock2, connection_timeout_sec,
2689-
connection_timeout_usec)) {
2700+
if (is_connection_error()) {
26902701
error = Error::Connection;
26912702
return false;
26922703
}
2704+
error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
2705+
connection_timeout_usec);
2706+
if (error != Error::Success) {
2707+
return false;
2708+
}
26932709
}
26942710

26952711
set_nonblocking(sock2, false);

test/test.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,14 @@ TEST(ConnectionErrorTest, Timeout) {
622622
#endif
623623
cli.set_connection_timeout(std::chrono::seconds(2));
624624

625+
// only probe one address type so that the error reason
626+
// correlates to the timed-out IPv4, not the unsupported
627+
// IPv6 connection attempt
628+
cli.set_address_family(AF_INET);
629+
625630
auto res = cli.Get("/");
626631
ASSERT_TRUE(!res);
627-
EXPECT_TRUE(res.error() == Error::Connection);
632+
EXPECT_EQ(Error::ConnectionTimeout, res.error());
628633
}
629634

630635
TEST(CancelTest, NoCancel_Online) {

0 commit comments

Comments
 (0)