libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_shutdown_select.c (10289B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2016 Karlson2k (Evgeny Grin)
      4 
      5      libmicrohttpd is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libmicrohttpd is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libmicrohttpd; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 
     21 /**
     22  * @file microhttpd/test_shutdown_select.c
     23  * @brief  Test whether shutdown socket triggers select()/poll()
     24  * @details On some platforms shutting down the socket in one thread
     25  *          triggers select() or poll() waiting for this socket in
     26  *          other thread. libmicrohttpd depends on this behavior on
     27  *          these platforms. This program check whether select()
     28  *          and poll() (if available) work as expected.
     29  * @author Karlson2k (Evgeny Grin)
     30  */
     31 
     32 #include "MHD_config.h"
     33 #include "platform.h"
     34 #include "mhd_sockets.h"
     35 #include <stdlib.h>
     36 #include <stdio.h>
     37 #include <errno.h>
     38 
     39 #ifdef HAVE_UNISTD_H
     40 #include <unistd.h>
     41 #endif /* HAVE_UNISTD_H */
     42 
     43 #ifdef HAVE_TIME_H
     44 #include <time.h>
     45 #endif /* HAVE_TIME_H */
     46 
     47 #if defined(MHD_USE_POSIX_THREADS)
     48 #include <pthread.h>
     49 #endif /* MHD_USE_POSIX_THREADS */
     50 
     51 #if defined(MHD_WINSOCK_SOCKETS)
     52 #include <winsock2.h>
     53 #include <windows.h>
     54 #define sock_errno (WSAGetLastError ())
     55 #elif defined(MHD_POSIX_SOCKETS)
     56 #ifdef HAVE_SYS_TYPES_H
     57 #include <sys/types.h>
     58 #endif /* HAVE_SYS_TYPES_H */
     59 #ifdef HAVE_SYS_SOCKET_H
     60 #include <sys/socket.h>
     61 #endif /* HAVE_SYS_SOCKET_H */
     62 #ifdef HAVE_NETINET_IN_H
     63 #include <netinet/in.h>
     64 #endif /* HAVE_NETINET_IN_H */
     65 #ifdef HAVE_ARPA_INET_H
     66 #include <arpa/inet.h>
     67 #endif /* HAVE_ARPA_INET_H */
     68 #ifdef HAVE_SYS_SELECT_H
     69 #include <sys/select.h>
     70 #endif /* HAVE_SYS_SELECT_H */
     71 #if defined(HAVE_POLL) && defined(HAVE_POLL_H)
     72 #include <poll.h>
     73 #endif /* HAVE_POLL && HAVE_POLL_H */
     74 #define sock_errno (errno)
     75 #endif /* MHD_POSIX_SOCKETS */
     76 
     77 #ifdef HAVE_STDBOOL_H
     78 #include <stdbool.h>
     79 #endif /* HAVE_STDBOOL_H */
     80 
     81 #include "mhd_threads.h"
     82 
     83 #ifndef SOMAXCONN
     84 #define SOMAXCONN 511
     85 #endif /* ! SOMAXCONN */
     86 
     87 #if ! defined(SHUT_RDWR) && defined(SD_BOTH)
     88 #define SHUT_RDWR SD_BOTH
     89 #endif
     90 
     91 static bool check_err;
     92 
     93 
     94 static bool
     95 has_in_name (const char *prog_name, const char *marker)
     96 {
     97   size_t name_pos;
     98   size_t pos;
     99 
    100   if (! prog_name || ! marker)
    101     return 0;
    102 
    103   pos = 0;
    104   name_pos = 0;
    105   while (prog_name[pos])
    106   {
    107     if ('/' == prog_name[pos])
    108       name_pos = pos + 1;
    109 #if defined(_WIN32) || defined(__CYGWIN__)
    110     else if ('\\' == prog_name[pos])
    111       name_pos = pos + 1;
    112 #endif /* _WIN32 || __CYGWIN__ */
    113     pos++;
    114   }
    115   if (name_pos == pos)
    116     return true;
    117   return strstr (prog_name + name_pos, marker) != NULL;
    118 }
    119 
    120 
    121 static MHD_socket
    122 start_socket_listen (int domain)
    123 {
    124 /* Create sockets similarly to daemon.c */
    125   MHD_socket fd;
    126   int cloexec_set;
    127   struct sockaddr_in sock_addr;
    128   socklen_t addrlen;
    129 
    130 #ifdef MHD_WINSOCK_SOCKETS
    131   unsigned long flags = 1;
    132 #else  /* MHD_POSIX_SOCKETS */
    133   int flags;
    134 #endif /* MHD_POSIX_SOCKETS */
    135 
    136 #if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC)
    137   fd = socket (domain, SOCK_STREAM | SOCK_CLOEXEC, 0);
    138   cloexec_set = 1;
    139 #elif defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT)
    140   fd = WSASocketW (domain, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT);
    141   cloexec_set = 1;
    142 #else  /* !SOCK_CLOEXEC */
    143   fd = socket (domain, SOCK_STREAM, 0);
    144   cloexec_set = 0;
    145 #endif /* !SOCK_CLOEXEC */
    146   if ( (MHD_INVALID_SOCKET == fd) && (cloexec_set) )
    147   {
    148     fd = socket (domain, SOCK_STREAM, 0);
    149     cloexec_set = 0;
    150   }
    151   if (MHD_INVALID_SOCKET == fd)
    152   {
    153     fprintf (stderr, "Can't create socket: %u\n",
    154              (unsigned) sock_errno);
    155     return MHD_INVALID_SOCKET;
    156   }
    157 
    158   if (! cloexec_set)
    159   {
    160 #ifdef MHD_WINSOCK_SOCKETS
    161     if (! SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0))
    162       fprintf (stderr, "Failed to make socket non-inheritable: %u\n",
    163                (unsigned int) GetLastError ());
    164 #else  /* MHD_POSIX_SOCKETS */
    165     flags = fcntl (fd, F_GETFD);
    166     if ( ( (-1 == flags) ||
    167            ( (flags != (flags | FD_CLOEXEC)) &&
    168              (0 != fcntl (fd, F_SETFD, flags | FD_CLOEXEC)) ) ) )
    169       fprintf (stderr, "Failed to make socket non-inheritable: %s\n",
    170                MHD_socket_last_strerr_ ());
    171 #endif /* MHD_POSIX_SOCKETS */
    172   }
    173 
    174   memset (&sock_addr, 0, sizeof (struct sockaddr_in));
    175   sock_addr.sin_family = AF_INET;
    176   sock_addr.sin_port = htons (0);
    177 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
    178   sock_addr.sin_len = sizeof (struct sockaddr_in);
    179 #endif
    180   addrlen = sizeof (struct sockaddr_in);
    181 
    182   if (bind (fd, (const struct sockaddr *) &sock_addr, addrlen) < 0)
    183   {
    184     fprintf (stderr, "Failed to bind socket: %u\n",
    185              (unsigned) sock_errno);
    186     MHD_socket_close_chk_ (fd);
    187     return MHD_INVALID_SOCKET;
    188   }
    189 
    190 #ifdef MHD_WINSOCK_SOCKETS
    191   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    192   {
    193     fprintf (stderr, "Failed to make socket non-blocking: %u\n",
    194              (unsigned) sock_errno);
    195     MHD_socket_close_chk_ (fd);
    196     return MHD_INVALID_SOCKET;
    197   }
    198 #else  /* MHD_POSIX_SOCKETS */
    199   flags = fcntl (fd, F_GETFL);
    200   if ( ( (-1 == flags) ||
    201          ( (flags != (flags | O_NONBLOCK)) &&
    202            (0 != fcntl (fd, F_SETFL, flags | O_NONBLOCK)) ) ) )
    203   {
    204     fprintf (stderr, "Failed to make socket non-blocking: %s\n",
    205              MHD_socket_last_strerr_ ());
    206     MHD_socket_close_chk_ (fd);
    207     return MHD_INVALID_SOCKET;
    208   }
    209 #endif /* MHD_POSIX_SOCKETS */
    210 
    211   if (listen (fd, SOMAXCONN) < 0)
    212   {
    213     fprintf (stderr, "Failed to listen on socket: %u\n",
    214              (unsigned) sock_errno);
    215     MHD_socket_close_chk_ (fd);
    216     return MHD_INVALID_SOCKET;
    217   }
    218 
    219   return fd;
    220 }
    221 
    222 
    223 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
    224 select_thread (void *data)
    225 {
    226   /* use select() like in daemon.c */
    227   MHD_socket listen_sock = *((MHD_socket *) data);
    228   fd_set rs, ws;
    229   struct timeval timeout;
    230 
    231   FD_ZERO (&rs);
    232   FD_ZERO (&ws);
    233   FD_SET (listen_sock, &rs);
    234   timeout.tv_usec = 0;
    235   timeout.tv_sec = 7;
    236 
    237   check_err = (0 > MHD_SYS_select_ (listen_sock + 1, &rs, &ws, NULL, &timeout));
    238 
    239   return (MHD_THRD_RTRN_TYPE_) 0;
    240 }
    241 
    242 
    243 #ifdef HAVE_POLL
    244 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
    245 poll_thread (void *data)
    246 {
    247   /* use poll() like in daemon.c */
    248   struct pollfd p[1];
    249   MHD_socket listen_sock = *((MHD_socket *) data);
    250 
    251   p[0].fd = listen_sock;
    252   p[0].events = POLLIN;
    253   p[0].revents = 0;
    254 
    255   check_err = (0 > MHD_sys_poll_ (p, 1, 7000));
    256 
    257   return (MHD_THRD_RTRN_TYPE_) 0;
    258 }
    259 
    260 
    261 #endif /* HAVE_POLL */
    262 
    263 
    264 static void
    265 local_sleep (unsigned seconds)
    266 {
    267 #if defined(_WIN32) && ! defined(__CYGWIN__)
    268   Sleep (seconds * 1000);
    269 #else
    270   unsigned seconds_left = seconds;
    271   do
    272   {
    273     seconds_left = sleep (seconds_left);
    274   } while (seconds_left > 0);
    275 #endif
    276 }
    277 
    278 
    279 int
    280 main (int argc, char *const *argv)
    281 {
    282   int i;
    283   time_t start_t, end_t;
    284   int result = 0;
    285   MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ * test_func)(void *data);
    286 #ifdef MHD_WINSOCK_SOCKETS
    287   WORD ver_req;
    288   WSADATA wsa_data;
    289   int err;
    290 #endif /* MHD_WINSOCK_SOCKETS */
    291   bool test_poll;
    292   bool must_ignore;
    293   (void) argc; /* Unused. Silent compiler warning. */
    294 
    295   test_poll = has_in_name (argv[0], "_poll");
    296   must_ignore = has_in_name (argv[0], "_ignore");
    297   if (! test_poll)
    298     test_func = &select_thread;
    299   else
    300   {
    301 #ifndef HAVE_POLL
    302     return 77;
    303 #else  /* ! HAVE_POLL */
    304     test_func = &poll_thread;
    305 #endif /* ! HAVE_POLL */
    306   }
    307 
    308 #ifdef MHD_WINSOCK_SOCKETS
    309   ver_req = MAKEWORD (2, 2);
    310 
    311   err = WSAStartup (ver_req, &wsa_data);
    312   if ((err != 0) || (MAKEWORD (2, 2) != wsa_data.wVersion))
    313   {
    314     printf ("WSAStartup() failed\n");
    315     WSACleanup ();
    316     return 99;
    317   }
    318 #endif /* MHD_WINSOCK_SOCKETS */
    319 
    320   /* try several times to ensure that accidental incoming connection
    321    * didn't interfere with test results
    322    */
    323   for (i = 0; i < 5 && result == 0; i++)
    324   {
    325     MHD_thread_handle_native_ sel_thrd;
    326     /* fprintf(stdout, "Creating, binding and listening socket...\n"); */
    327     MHD_socket listen_socket = start_socket_listen (AF_INET);
    328     if (MHD_INVALID_SOCKET == listen_socket)
    329       return 99;
    330 
    331     check_err = true;
    332     /* fprintf (stdout, "Starting select() thread...\n"); */
    333 #if defined(MHD_USE_POSIX_THREADS)
    334     if (0 != pthread_create (&sel_thrd, NULL, test_func, &listen_socket))
    335     {
    336       MHD_socket_close_chk_ (listen_socket);
    337       fprintf (stderr, "Can't start thread\n");
    338       return 99;
    339     }
    340 #elif defined(MHD_USE_W32_THREADS)
    341     sel_thrd = (HANDLE) (uintptr_t)
    342                _beginthreadex (NULL, 0, test_func, &listen_socket, 0, NULL);
    343     if (0 == (sel_thrd))
    344     {
    345       MHD_socket_close_chk_ (listen_socket);
    346       fprintf (stderr, "Can't start select() thread\n");
    347       return 99;
    348     }
    349 #else
    350 #error No threading lib available
    351 #endif
    352     /* fprintf (stdout, "Waiting...\n"); */
    353     local_sleep (1);  /* make sure that select() is started */
    354 
    355     /* fprintf (stdout, "Shutting down socket...\n"); */
    356     start_t = time (NULL);
    357     shutdown (listen_socket, SHUT_RDWR);
    358 
    359     /* fprintf (stdout, "Waiting for thread to finish...\n"); */
    360     if (! MHD_join_thread_ (sel_thrd))
    361     {
    362       MHD_socket_close_chk_ (listen_socket);
    363       fprintf (stderr, "Can't join select() thread\n");
    364       return 99;
    365     }
    366     if (check_err)
    367     {
    368       MHD_socket_close_chk_ (listen_socket);
    369       fprintf (stderr, "Error in waiting thread\n");
    370       return 99;
    371     }
    372     end_t = time (NULL);
    373     /* fprintf (stdout, "Thread finished.\n"); */
    374     MHD_socket_close_chk_ (listen_socket);
    375 
    376     if ((start_t == (time_t) -1) || (end_t == (time_t) -1) )
    377     {
    378       MHD_socket_close_chk_ (listen_socket);
    379       fprintf (stderr, "Can't get current time\n");
    380       return 99;
    381     }
    382     if (end_t - start_t > 3)
    383       result++;
    384   }
    385 
    386 #ifdef MHD_WINSOCK_SOCKETS
    387   WSACleanup ();
    388 #endif /* MHD_WINSOCK_SOCKETS */
    389 
    390   return must_ignore ? (! result) : (result);
    391 }