66#include < array>
77#include < cctype>
88#include < cstdint>
9+ #include < cerrno>
910#include < iostream>
1011#include < limits>
1112#include < optional>
@@ -23,6 +24,7 @@ constexpr SocketHandle kInvalidSocket = INVALID_SOCKET;
2324#else
2425#include < arpa/inet.h>
2526#include < netinet/in.h>
27+ #include < sys/select.h>
2628#include < sys/socket.h>
2729#include < unistd.h>
2830using SocketHandle = int ;
@@ -273,6 +275,33 @@ void handle_client(SocketHandle client, IHttpHandler & handler) {
273275 }
274276}
275277
278+ bool wait_for_client (SocketHandle socket, int timeout_ms) {
279+ fd_set read_set;
280+ FD_ZERO (&read_set);
281+ FD_SET (socket, &read_set);
282+
283+ timeval timeout{};
284+ timeout.tv_sec = timeout_ms / 1000 ;
285+ timeout.tv_usec = (timeout_ms % 1000 ) * 1000 ;
286+
287+ #ifdef _WIN32
288+ const int ready = select (0 , &read_set, nullptr , nullptr , &timeout);
289+ #else
290+ const int ready = select (socket + 1 , &read_set, nullptr , nullptr , &timeout);
291+ #endif
292+ if (ready < 0 ) {
293+ #ifdef _WIN32
294+ if (WSAGetLastError () == WSAEINTR ) {
295+ #else
296+ if (errno == EINTR ) {
297+ #endif
298+ return false ;
299+ }
300+ throw std::runtime_error (" server select failed" );
301+ }
302+ return ready > 0 && FD_ISSET (socket, &read_set);
303+ }
304+
276305} // namespace
277306
278307HttpResponse json_response (std::string body, int status) {
@@ -285,11 +314,14 @@ HttpResponse error_response(int status, const std::string & message, const std::
285314 return json_response (body, status);
286315}
287316
288- void serve_http (const std::string & host, int port, IHttpHandler & handler) {
317+ void serve_http (const std::string & host, int port, IHttpHandler & handler, ShutdownRequested shutdown_requested ) {
289318 SocketRuntime sockets;
290319 auto listen_socket = bind_listen_socket (host, port);
291320 std::cout << " audiocpp_server listening on http://" << host << " :" << port << " \n " ;
292- while (true ) {
321+ while (!shutdown_requested ()) {
322+ if (!wait_for_client (listen_socket.get (), 250 )) {
323+ continue ;
324+ }
293325 sockaddr_in client_addr{};
294326#ifdef _WIN32
295327 int client_len = sizeof (client_addr);
@@ -301,10 +333,20 @@ void serve_http(const std::string & host, int port, IHttpHandler & handler) {
301333 reinterpret_cast <sockaddr *>(&client_addr),
302334 &client_len);
303335 if (client == kInvalidSocket ) {
336+ #ifdef _WIN32
337+ const int error = WSAGetLastError ();
338+ const bool transient = error == WSAEINTR || error == WSAEWOULDBLOCK ;
339+ #else
340+ const bool transient = errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ;
341+ #endif
342+ if (shutdown_requested () || transient) {
343+ continue ;
344+ }
304345 throw std::runtime_error (" accept failed" );
305346 }
306347 std::thread (handle_client, client, std::ref (handler)).detach ();
307348 }
349+ std::cout << " audiocpp_server stopped\n " ;
308350}
309351
310352} // namespace minitts::server
0 commit comments