Report connection timeout as separate event (#1171)

This commit is contained in:
Gregor Jasny 2022-01-26 23:32:40 +01:00 committed by GitHub
parent e5cacb465d
commit 87e03dd1ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 12 deletions

View file

@ -799,6 +799,7 @@ enum class Error {
SSLServerVerification,
UnsupportedMultipartBoundaryChars,
Compression,
ConnectionTimeout,
};
std::string to_string(const Error error);
@ -1594,6 +1595,7 @@ inline std::string to_string(const Error error) {
case Error::UnsupportedMultipartBoundaryChars:
return "UnsupportedMultipartBoundaryChars";
case Error::Compression: return "Compression";
case Error::ConnectionTimeout: return "ConnectionTimeout";
case Error::Unknown: return "Unknown";
default: break;
}
@ -2313,7 +2315,7 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#endif
}
inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@ -2323,17 +2325,23 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
if (poll_res == 0) {
return Error::ConnectionTimeout;
}
if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
return res >= 0 && !error;
auto successful = res >= 0 && !error;
return successful ? Error::Success : Error::Connection;
}
return false;
return Error::Connection;
#else
#ifndef _WIN32
if (sock >= FD_SETSIZE) { return false; }
if (sock >= FD_SETSIZE) { return Error::Connection; }
#endif
fd_set fdsr;
@ -2351,14 +2359,19 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
});
if (ret == 0) {
return Error::ConnectionTimeout;
}
if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
socklen_t len = sizeof(error);
return getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len) >= 0 &&
!error;
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
auto successful = res >= 0 && !error;
return successful ? Error::Success : Error::Connection;
}
return false;
return Error::Connection;
#endif
}
@ -2684,12 +2697,15 @@ inline socket_t create_client_socket(
::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
if (ret < 0) {
if (is_connection_error() ||
!wait_until_socket_is_ready(sock2, connection_timeout_sec,
connection_timeout_usec)) {
if (is_connection_error()) {
error = Error::Connection;
return false;
}
error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
connection_timeout_usec);
if (error != Error::Success) {
return false;
}
}
set_nonblocking(sock2, false);

View file

@ -622,9 +622,14 @@ TEST(ConnectionErrorTest, Timeout) {
#endif
cli.set_connection_timeout(std::chrono::seconds(2));
// only probe one address type so that the error reason
// correlates to the timed-out IPv4, not the unsupported
// IPv6 connection attempt
cli.set_address_family(AF_INET);
auto res = cli.Get("/");
ASSERT_TRUE(!res);
EXPECT_TRUE(res.error() == Error::Connection);
EXPECT_EQ(Error::ConnectionTimeout, res.error());
}
TEST(CancelTest, NoCancel_Online) {