libmicrohttpd

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

test_shutdown_select.c (10456B)


      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 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     84    test into a marked, classifiable test error (TESTING.md, P5). */
     85 #include "mhd_panic_tripwire.h"
     86 
     87 #ifndef SOMAXCONN
     88 #define SOMAXCONN 511
     89 #endif /* ! SOMAXCONN */
     90 
     91 #if ! defined(SHUT_RDWR) && defined(SD_BOTH)
     92 #define SHUT_RDWR SD_BOTH
     93 #endif
     94 
     95 static bool check_err;
     96 
     97 
     98 static bool
     99 has_in_name (const char *prog_name, const char *marker)
    100 {
    101   size_t name_pos;
    102   size_t pos;
    103 
    104   if (! prog_name || ! marker)
    105     return 0;
    106 
    107   pos = 0;
    108   name_pos = 0;
    109   while (prog_name[pos])
    110   {
    111     if ('/' == prog_name[pos])
    112       name_pos = pos + 1;
    113 #if defined(_WIN32) || defined(__CYGWIN__)
    114     else if ('\\' == prog_name[pos])
    115       name_pos = pos + 1;
    116 #endif /* _WIN32 || __CYGWIN__ */
    117     pos++;
    118   }
    119   if (name_pos == pos)
    120     return true;
    121   return strstr (prog_name + name_pos, marker) != NULL;
    122 }
    123 
    124 
    125 static MHD_socket
    126 start_socket_listen (int domain)
    127 {
    128 /* Create sockets similarly to daemon.c */
    129   MHD_socket fd;
    130   int cloexec_set;
    131   struct sockaddr_in sock_addr;
    132   socklen_t addrlen;
    133 
    134 #ifdef MHD_WINSOCK_SOCKETS
    135   unsigned long flags = 1;
    136 #else  /* MHD_POSIX_SOCKETS */
    137   int flags;
    138 #endif /* MHD_POSIX_SOCKETS */
    139 
    140 #if defined(MHD_POSIX_SOCKETS) && defined(SOCK_CLOEXEC)
    141   fd = socket (domain, SOCK_STREAM | SOCK_CLOEXEC, 0);
    142   cloexec_set = 1;
    143 #elif defined(MHD_WINSOCK_SOCKETS) && defined(WSA_FLAG_NO_HANDLE_INHERIT)
    144   fd = WSASocketW (domain, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT);
    145   cloexec_set = 1;
    146 #else  /* !SOCK_CLOEXEC */
    147   fd = socket (domain, SOCK_STREAM, 0);
    148   cloexec_set = 0;
    149 #endif /* !SOCK_CLOEXEC */
    150   if ( (MHD_INVALID_SOCKET == fd) && (cloexec_set) )
    151   {
    152     fd = socket (domain, SOCK_STREAM, 0);
    153     cloexec_set = 0;
    154   }
    155   if (MHD_INVALID_SOCKET == fd)
    156   {
    157     fprintf (stderr, "Can't create socket: %u\n",
    158              (unsigned) sock_errno);
    159     return MHD_INVALID_SOCKET;
    160   }
    161 
    162   if (! cloexec_set)
    163   {
    164 #ifdef MHD_WINSOCK_SOCKETS
    165     if (! SetHandleInformation ((HANDLE) fd, HANDLE_FLAG_INHERIT, 0))
    166       fprintf (stderr, "Failed to make socket non-inheritable: %u\n",
    167                (unsigned int) GetLastError ());
    168 #else  /* MHD_POSIX_SOCKETS */
    169     flags = fcntl (fd, F_GETFD);
    170     if ( ( (-1 == flags) ||
    171            ( (flags != (flags | FD_CLOEXEC)) &&
    172              (0 != fcntl (fd, F_SETFD, flags | FD_CLOEXEC)) ) ) )
    173       fprintf (stderr, "Failed to make socket non-inheritable: %s\n",
    174                MHD_socket_last_strerr_ ());
    175 #endif /* MHD_POSIX_SOCKETS */
    176   }
    177 
    178   memset (&sock_addr, 0, sizeof (struct sockaddr_in));
    179   sock_addr.sin_family = AF_INET;
    180   sock_addr.sin_port = htons (0);
    181 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
    182   sock_addr.sin_len = sizeof (struct sockaddr_in);
    183 #endif
    184   addrlen = sizeof (struct sockaddr_in);
    185 
    186   if (bind (fd, (const struct sockaddr *) &sock_addr, addrlen) < 0)
    187   {
    188     fprintf (stderr, "Failed to bind socket: %u\n",
    189              (unsigned) sock_errno);
    190     MHD_socket_close_chk_ (fd);
    191     return MHD_INVALID_SOCKET;
    192   }
    193 
    194 #ifdef MHD_WINSOCK_SOCKETS
    195   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    196   {
    197     fprintf (stderr, "Failed to make socket non-blocking: %u\n",
    198              (unsigned) sock_errno);
    199     MHD_socket_close_chk_ (fd);
    200     return MHD_INVALID_SOCKET;
    201   }
    202 #else  /* MHD_POSIX_SOCKETS */
    203   flags = fcntl (fd, F_GETFL);
    204   if ( ( (-1 == flags) ||
    205          ( (flags != (flags | O_NONBLOCK)) &&
    206            (0 != fcntl (fd, F_SETFL, flags | O_NONBLOCK)) ) ) )
    207   {
    208     fprintf (stderr, "Failed to make socket non-blocking: %s\n",
    209              MHD_socket_last_strerr_ ());
    210     MHD_socket_close_chk_ (fd);
    211     return MHD_INVALID_SOCKET;
    212   }
    213 #endif /* MHD_POSIX_SOCKETS */
    214 
    215   if (listen (fd, SOMAXCONN) < 0)
    216   {
    217     fprintf (stderr, "Failed to listen on socket: %u\n",
    218              (unsigned) sock_errno);
    219     MHD_socket_close_chk_ (fd);
    220     return MHD_INVALID_SOCKET;
    221   }
    222 
    223   return fd;
    224 }
    225 
    226 
    227 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
    228 select_thread (void *data)
    229 {
    230   /* use select() like in daemon.c */
    231   MHD_socket listen_sock = *((MHD_socket *) data);
    232   fd_set rs, ws;
    233   struct timeval timeout;
    234 
    235   FD_ZERO (&rs);
    236   FD_ZERO (&ws);
    237   FD_SET (listen_sock, &rs);
    238   timeout.tv_usec = 0;
    239   timeout.tv_sec = 7;
    240 
    241   check_err = (0 > MHD_SYS_select_ (listen_sock + 1, &rs, &ws, NULL, &timeout));
    242 
    243   return (MHD_THRD_RTRN_TYPE_) 0;
    244 }
    245 
    246 
    247 #ifdef HAVE_POLL
    248 static MHD_THRD_RTRN_TYPE_ MHD_THRD_CALL_SPEC_
    249 poll_thread (void *data)
    250 {
    251   /* use poll() like in daemon.c */
    252   struct pollfd p[1];
    253   MHD_socket listen_sock = *((MHD_socket *) data);
    254 
    255   p[0].fd = listen_sock;
    256   p[0].events = POLLIN;
    257   p[0].revents = 0;
    258 
    259   check_err = (0 > MHD_sys_poll_ (p, 1, 7000));
    260 
    261   return (MHD_THRD_RTRN_TYPE_) 0;
    262 }
    263 
    264 
    265 #endif /* HAVE_POLL */
    266 
    267 
    268 static void
    269 local_sleep (unsigned seconds)
    270 {
    271 #if defined(_WIN32) && ! defined(__CYGWIN__)
    272   Sleep (seconds * 1000);
    273 #else
    274   unsigned seconds_left = seconds;
    275   do
    276   {
    277     seconds_left = sleep (seconds_left);
    278   } while (seconds_left > 0);
    279 #endif
    280 }
    281 
    282 
    283 int
    284 main (int argc, char *const *argv)
    285 {
    286   int i;
    287   time_t start_t, end_t;
    288   int result = 0;
    289   MHD_THRD_RTRN_TYPE_ (MHD_THRD_CALL_SPEC_ * test_func)(void *data);
    290 #ifdef MHD_WINSOCK_SOCKETS
    291   WORD ver_req;
    292   WSADATA wsa_data;
    293   int err;
    294 #endif /* MHD_WINSOCK_SOCKETS */
    295   bool test_poll;
    296   bool must_ignore;
    297   (void) argc; /* Unused. Silent compiler warning. */
    298 
    299   test_poll = has_in_name (argv[0], "_poll");
    300   must_ignore = has_in_name (argv[0], "_ignore");
    301   if (! test_poll)
    302     test_func = &select_thread;
    303   else
    304   {
    305 #ifndef HAVE_POLL
    306     return 77;
    307 #else  /* ! HAVE_POLL */
    308     test_func = &poll_thread;
    309 #endif /* ! HAVE_POLL */
    310   }
    311 
    312 #ifdef MHD_WINSOCK_SOCKETS
    313   ver_req = MAKEWORD (2, 2);
    314 
    315   err = WSAStartup (ver_req, &wsa_data);
    316   if ((err != 0) || (MAKEWORD (2, 2) != wsa_data.wVersion))
    317   {
    318     printf ("WSAStartup() failed\n");
    319     WSACleanup ();
    320     return 99;
    321   }
    322 #endif /* MHD_WINSOCK_SOCKETS */
    323 
    324   /* try several times to ensure that accidental incoming connection
    325    * didn't interfere with test results
    326    */
    327   for (i = 0; i < 5 && result == 0; i++)
    328   {
    329     MHD_thread_handle_native_ sel_thrd;
    330     /* fprintf(stdout, "Creating, binding and listening socket...\n"); */
    331     MHD_socket listen_socket = start_socket_listen (AF_INET);
    332     if (MHD_INVALID_SOCKET == listen_socket)
    333       return 99;
    334 
    335     check_err = true;
    336     /* fprintf (stdout, "Starting select() thread...\n"); */
    337 #if defined(MHD_USE_POSIX_THREADS)
    338     if (0 != pthread_create (&sel_thrd, NULL, test_func, &listen_socket))
    339     {
    340       MHD_socket_close_chk_ (listen_socket);
    341       fprintf (stderr, "Can't start thread\n");
    342       return 99;
    343     }
    344 #elif defined(MHD_USE_W32_THREADS)
    345     sel_thrd = (HANDLE) (uintptr_t)
    346                _beginthreadex (NULL, 0, test_func, &listen_socket, 0, NULL);
    347     if (0 == (sel_thrd))
    348     {
    349       MHD_socket_close_chk_ (listen_socket);
    350       fprintf (stderr, "Can't start select() thread\n");
    351       return 99;
    352     }
    353 #else
    354 #error No threading lib available
    355 #endif
    356     /* fprintf (stdout, "Waiting...\n"); */
    357     local_sleep (1);  /* make sure that select() is started */
    358 
    359     /* fprintf (stdout, "Shutting down socket...\n"); */
    360     start_t = time (NULL);
    361     shutdown (listen_socket, SHUT_RDWR);
    362 
    363     /* fprintf (stdout, "Waiting for thread to finish...\n"); */
    364     if (! MHD_join_thread_ (sel_thrd))
    365     {
    366       MHD_socket_close_chk_ (listen_socket);
    367       fprintf (stderr, "Can't join select() thread\n");
    368       return 99;
    369     }
    370     if (check_err)
    371     {
    372       MHD_socket_close_chk_ (listen_socket);
    373       fprintf (stderr, "Error in waiting thread\n");
    374       return 99;
    375     }
    376     end_t = time (NULL);
    377     /* fprintf (stdout, "Thread finished.\n"); */
    378     MHD_socket_close_chk_ (listen_socket);
    379 
    380     if ((start_t == (time_t) -1) || (end_t == (time_t) -1) )
    381     {
    382       MHD_socket_close_chk_ (listen_socket);
    383       fprintf (stderr, "Can't get current time\n");
    384       return 99;
    385     }
    386     if (end_t - start_t > 3)
    387       result++;
    388   }
    389 
    390 #ifdef MHD_WINSOCK_SOCKETS
    391   WSACleanup ();
    392 #endif /* MHD_WINSOCK_SOCKETS */
    393 
    394   return must_ignore ? (! result) : (result);
    395 }