From 5536d4c1ffb4f0725b6fca347de79918821c806a Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 6 Apr 2018 17:02:37 -0400 Subject: [PATCH] Fix #44 --- httplib.h | 36 ++++++++++++++++++++------------ test/test.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/httplib.h b/httplib.h index 0a60c4b..1716d0e 100644 --- a/httplib.h +++ b/httplib.h @@ -437,25 +437,32 @@ inline int select_read(socket_t sock, size_t sec, size_t usec) return select(sock + 1, &fds, NULL, NULL, &tv); } -inline bool is_socket_writable(socket_t sock, size_t sec, size_t usec) +inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec) { - fd_set fdsw; - FD_ZERO(&fdsw); - FD_SET(sock, &fdsw); + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); - fd_set fdse; - FD_ZERO(&fdse); - FD_SET(sock, &fdse); + auto fdsw = fdsr; + auto fdse = fdsr; timeval tv; tv.tv_sec = sec; tv.tv_usec = usec; - if (select(sock + 1, NULL, &fdsw, &fdse, &tv) <= 0) { + if (select(sock + 1, &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error) { + return false; + } + } else { return false; } - return FD_ISSET(sock, &fdsw) != 0; + return true; } template @@ -1690,13 +1697,16 @@ inline socket_t Client::create_client_socket() const detail::set_nonblocking(sock, true); auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen); - if (ret == -1 && detail::is_connection_error()) { - return false; + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; + } } detail::set_nonblocking(sock, false); - - return detail::is_socket_writable(sock, timeout_sec_, 0); + return true; }); } diff --git a/test/test.cc b/test/test.cc index 3e4daf1..815246b 100644 --- a/test/test.cc +++ b/test/test.cc @@ -121,7 +121,7 @@ TEST(GetHeaderValueTest, Range) void testChunkedEncoding(httplib::HttpVersion ver) { auto host = "www.httpwatch.com"; - auto sec = 5; + auto sec = 2; #ifdef CPPHTTPLIB_OPENSSL_SUPPORT auto port = 443; @@ -186,6 +186,60 @@ TEST(RangeTest, FromHTTPBin) } } +TEST(ConnectionErrorTest, InvalidHost) +{ + auto host = "abcde.com"; + auto sec = 2; + auto ver = httplib::HttpVersion::v1_1; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 443; + httplib::SSLClient cli(host, port, sec, ver); +#else + auto port = 80; + httplib::Client cli(host, port, sec, ver); +#endif + + auto res = cli.get("/"); + ASSERT_TRUE(res == nullptr); +} + +TEST(ConnectionErrorTest, InvalidPort) +{ + auto host = "localhost"; + auto sec = 2; + auto ver = httplib::HttpVersion::v1_1; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 44380; + httplib::SSLClient cli(host, port, sec, ver); +#else + auto port = 8080; + httplib::Client cli(host, port, sec, ver); +#endif + + auto res = cli.get("/"); + ASSERT_TRUE(res == nullptr); +} + +TEST(ConnectionErrorTest, Timeout) +{ + auto host = "google.com"; + auto sec = 2; + auto ver = httplib::HttpVersion::v1_1; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT + auto port = 44380; + httplib::SSLClient cli(host, port, sec, ver); +#else + auto port = 8080; + httplib::Client cli(host, port, sec, ver); +#endif + + auto res = cli.get("/"); + ASSERT_TRUE(res == nullptr); +} + class ServerTest : public ::testing::Test { protected: ServerTest() @@ -584,7 +638,7 @@ TEST_F(ServerTest, GetMethodRemoteAddr) ASSERT_TRUE(res != nullptr); EXPECT_EQ(200, res->status); EXPECT_EQ("text/plain", res->get_header_value("Content-Type")); - EXPECT_EQ("::1", res->body); // NOTE: depends on user's environment... + EXPECT_TRUE(res->body == "::1" || res->body == "127.0.0.1"); } #ifdef CPPHTTPLIB_ZLIB_SUPPORT