yhirose 2 years ago
parent
commit
7aba2938d3
2 changed files with 57 additions and 93 deletions
  1. 17 6
      httplib.h
  2. 40 87
      test/test.cc

+ 17 - 6
httplib.h

@@ -743,6 +743,7 @@ public:
   bool listen(const std::string &host, int port, int socket_flags = 0);
   bool listen(const std::string &host, int port, int socket_flags = 0);
 
 
   bool is_running() const;
   bool is_running() const;
+  void wait_until_ready() const;
   void stop();
   void stop();
 
 
   std::function<TaskQueue *(void)> new_task_queue;
   std::function<TaskQueue *(void)> new_task_queue;
@@ -752,7 +753,7 @@ protected:
                        bool &connection_closed,
                        bool &connection_closed,
                        const std::function<void(Request &)> &setup_request);
                        const std::function<void(Request &)> &setup_request);
 
 
-  std::atomic<socket_t> svr_sock_;
+  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
   size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
   size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
   time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
   time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
   time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
   time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
@@ -816,7 +817,8 @@ private:
   };
   };
   std::vector<MountPointEntry> base_dirs_;
   std::vector<MountPointEntry> base_dirs_;
 
 
-  std::atomic<bool> is_running_;
+  std::atomic<bool> is_running_{false};
+  std::atomic<bool> done_{false};
   std::map<std::string, std::string> file_extension_and_mimetype_map_;
   std::map<std::string, std::string> file_extension_and_mimetype_map_;
   Handler file_request_handler_;
   Handler file_request_handler_;
   Handlers get_handlers_;
   Handlers get_handlers_;
@@ -5120,8 +5122,7 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
 // HTTP server implementation
 // HTTP server implementation
 inline Server::Server()
 inline Server::Server()
     : new_task_queue(
     : new_task_queue(
-          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
-      svr_sock_(INVALID_SOCKET), is_running_(false) {
+          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {
 #ifndef _WIN32
 #ifndef _WIN32
   signal(SIGPIPE, SIG_IGN);
   signal(SIGPIPE, SIG_IGN);
 #endif
 #endif
@@ -5334,15 +5335,25 @@ inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
   return bind_internal(host, 0, socket_flags);
   return bind_internal(host, 0, socket_flags);
 }
 }
 
 
-inline bool Server::listen_after_bind() { return listen_internal(); }
+inline bool Server::listen_after_bind() {
+  auto se = detail::scope_exit([&]() { done_ = true; });
+  return listen_internal();
+}
 
 
 inline bool Server::listen(const std::string &host, int port,
 inline bool Server::listen(const std::string &host, int port,
                            int socket_flags) {
                            int socket_flags) {
+  auto se = detail::scope_exit([&]() { done_ = true; });
   return bind_to_port(host, port, socket_flags) && listen_internal();
   return bind_to_port(host, port, socket_flags) && listen_internal();
 }
 }
 
 
 inline bool Server::is_running() const { return is_running_; }
 inline bool Server::is_running() const { return is_running_; }
 
 
+inline void Server::wait_until_ready() const {
+  while (!is_running() && !done_) {
+    std::this_thread::sleep_for(std::chrono::milliseconds{1});
+  }
+}
+
 inline void Server::stop() {
 inline void Server::stop() {
   if (is_running_) {
   if (is_running_) {
     assert(svr_sock_ != INVALID_SOCKET);
     assert(svr_sock_ != INVALID_SOCKET);
@@ -5733,6 +5744,7 @@ inline int Server::bind_internal(const std::string &host, int port,
 inline bool Server::listen_internal() {
 inline bool Server::listen_internal() {
   auto ret = true;
   auto ret = true;
   is_running_ = true;
   is_running_ = true;
+  auto se = detail::scope_exit([&]() { is_running_ = false; });
 
 
   {
   {
     std::unique_ptr<TaskQueue> task_queue(new_task_queue());
     std::unique_ptr<TaskQueue> task_queue(new_task_queue());
@@ -5804,7 +5816,6 @@ inline bool Server::listen_internal() {
     task_queue->shutdown();
     task_queue->shutdown();
   }
   }
 
 
-  is_running_ = false;
   return ret;
   return ret;
 }
 }
 
 

+ 40 - 87
test/test.cc

@@ -1007,9 +1007,7 @@ TEST(ReceiveSignals, Signal) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   ASSERT_TRUE(svr.is_running());
   ASSERT_TRUE(svr.is_running());
   pthread_kill(thread.native_handle(), SIGINT);
   pthread_kill(thread.native_handle(), SIGINT);
@@ -1084,9 +1082,7 @@ TEST(RedirectFromPageWithContent, Redirect) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -3762,9 +3758,7 @@ TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Only space and horizontal tab are whitespace. Make sure other whitespace-
   // Only space and horizontal tab are whitespace. Make sure other whitespace-
   // like characters are not treated the same - use vertical tab and escape.
   // like characters are not treated the same - use vertical tab and escape.
@@ -3802,9 +3796,7 @@ static void test_raw_request(const std::string &req,
     EXPECT_TRUE(listen_thread_ok);
     EXPECT_TRUE(listen_thread_ok);
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   ASSERT_TRUE(send_request(client_read_timeout_sec, req, out));
   ASSERT_TRUE(send_request(client_read_timeout_sec, req, out));
 }
 }
@@ -3939,9 +3931,7 @@ TEST(ServerStopTest, StopServerWithChunkedTransmission) {
   });
   });
 
 
   auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
   auto listen_thread = std::thread([&svr]() { svr.listen("localhost", PORT); });
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   Client client(HOST, PORT);
   Client client(HOST, PORT);
   const Headers headers = {{"Accept", "text/event-stream"}};
   const Headers headers = {{"Accept", "text/event-stream"}};
@@ -3968,9 +3958,7 @@ TEST(ServerStopTest, ClientAccessAfterServerDown) {
                       httplib::Response &res) { res.status = 200; });
                       httplib::Response &res) { res.status = 200; });
 
 
   auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
   auto thread = std::thread([&]() { svr.listen(HOST, PORT); });
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(5));
-  }
+  svr.wait_until_ready();
 
 
   Client cli(HOST, PORT);
   Client cli(HOST, PORT);
 
 
@@ -3987,6 +3975,17 @@ TEST(ServerStopTest, ClientAccessAfterServerDown) {
   ASSERT_FALSE(res);
   ASSERT_FALSE(res);
 }
 }
 
 
+TEST(ServerStopTest, ListenFailure) {
+  Server svr;
+  auto t = thread([&]() {
+    auto ret = svr.listen("????", PORT);
+    EXPECT_FALSE(ret);
+  });
+  svr.wait_until_ready();
+  svr.stop();
+  t.join();
+}
+
 TEST(StreamingTest, NoContentLengthStreaming) {
 TEST(StreamingTest, NoContentLengthStreaming) {
   Server svr;
   Server svr;
 
 
@@ -4008,9 +4007,7 @@ TEST(StreamingTest, NoContentLengthStreaming) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   Client client(HOST, PORT);
   Client client(HOST, PORT);
 
 
@@ -4039,9 +4036,7 @@ TEST(MountTest, Unmount) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -4094,9 +4089,7 @@ TEST(ExceptionTest, ThrowExceptionInHandler) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -4140,9 +4133,7 @@ TEST(KeepAliveTest, ReadTimeout) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -4270,9 +4261,7 @@ TEST(ClientProblemDetectionTest, ContentProvider) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -4312,9 +4301,7 @@ TEST(ErrorHandlerWithContentProviderTest, ErrorHandler) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -4355,9 +4342,7 @@ TEST(GetWithParametersTest, GetWithParameters) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -4407,9 +4392,7 @@ TEST(GetWithParametersTest, GetWithParameters2) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   Client cli("localhost", PORT);
   Client cli("localhost", PORT);
@@ -4464,9 +4447,7 @@ TEST(ServerDefaultHeadersTest, DefaultHeaders) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   Client cli("localhost", PORT);
   Client cli("localhost", PORT);
@@ -4500,9 +4481,7 @@ TEST(KeepAliveTest, ReadTimeoutSSL) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -5079,9 +5058,7 @@ TEST(SSLClientServerTest, DISABLED_LargeDataTransfer) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // client POST
   // client POST
   SSLClient cli("localhost", PORT);
   SSLClient cli("localhost", PORT);
@@ -5147,9 +5124,7 @@ TEST(ClientImplMethods, GetSocketTest) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(5));
-  }
+  svr.wait_until_ready();
 
 
   {
   {
     httplib::Client cli("http://127.0.0.1:3333");
     httplib::Client cli("http://127.0.0.1:3333");
@@ -5193,9 +5168,7 @@ TEST(ServerLargeContentTest, DISABLED_SendLargeContent) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   // Give GET time to get a few messages.
   // Give GET time to get a few messages.
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -5389,9 +5362,7 @@ TEST(MultipartFormDataTest, LargeData) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -5539,9 +5510,7 @@ TEST(MultipartFormDataTest, DataProviderItems) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -5627,9 +5596,7 @@ TEST(MultipartFormDataTest, BadHeader) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   const std::string body =
   const std::string body =
       "This is the preamble.  It is to be ignored, though it\r\n"
       "This is the preamble.  It is to be ignored, though it\r\n"
@@ -5673,9 +5640,7 @@ TEST(MultipartFormDataTest, WithPreamble) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
 
 
   const std::string body =
   const std::string body =
       "This is the preamble.  It is to be ignored, though it\r\n"
       "This is the preamble.  It is to be ignored, though it\r\n"
@@ -5747,9 +5712,7 @@ TEST(MultipartFormDataTest, PostCustomBoundary) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -5836,9 +5799,7 @@ TEST(MultipartFormDataTest, PutFormData) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -5903,9 +5864,7 @@ TEST(MultipartFormDataTest, PutFormDataCustomBoundary) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   std::this_thread::sleep_for(std::chrono::seconds(1));
   std::this_thread::sleep_for(std::chrono::seconds(1));
 
 
   {
   {
@@ -5990,9 +5949,7 @@ TEST_F(UnixSocketTest, pathname) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   ASSERT_TRUE(svr.is_running());
   ASSERT_TRUE(svr.is_running());
 
 
   client_GET(pathname_);
   client_GET(pathname_);
@@ -6017,9 +5974,7 @@ TEST_F(UnixSocketTest, PeerPid) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   ASSERT_TRUE(svr.is_running());
   ASSERT_TRUE(svr.is_running());
 
 
   client_GET(pathname_);
   client_GET(pathname_);
@@ -6046,9 +6001,7 @@ TEST_F(UnixSocketTest, abstract) {
     ASSERT_FALSE(svr.is_running());
     ASSERT_FALSE(svr.is_running());
   });
   });
 
 
-  while (!svr.is_running()) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(1));
-  }
+  svr.wait_until_ready();
   ASSERT_TRUE(svr.is_running());
   ASSERT_TRUE(svr.is_running());
 
 
   client_GET(abstract_addr);
   client_GET(abstract_addr);