libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

external-select.c (5356B)


      1 /* examples/external-select.c */
      2 
      3 /* Must be defined before including "microhttpd2.h" */
      4 #define MHD_APP_SOCKET_CNTX_TYPE struct AppSockContext
      5 #include <microhttpd2.h>
      6 #include <assert.h>
      7 #include <sys/select.h>
      8 
      9 
     10 /**
     11  * We keep the sockets we are waiting on in a DLL.
     12  */
     13 struct AppSockContext
     14 {
     15   struct AppSockContext *next;
     16   struct AppSockContext *prev;
     17   struct MHD_EventUpdateContext *ecb_cntx;
     18   MHD_Socket fd;
     19 };
     20 
     21 
     22 /**
     23  * Current read set.
     24  */
     25 static fd_set rs;
     26 
     27 /**
     28  * Current write set.
     29  */
     30 static fd_set ws;
     31 
     32 /**
     33  * Current error set.
     34  */
     35 static fd_set es;
     36 
     37 /**
     38  * Maximum FD in any set.
     39  */
     40 static int max_fd;
     41 
     42 /**
     43  * Head of our internal list of sockets to select() on.
     44  */
     45 static struct AppSockContext *head;
     46 
     47 
     48 /* This is the function MHD will call when the external event
     49    loop needs to change how it watches out for changes to
     50    some socket's state */
     51 static MHD_APP_SOCKET_CNTX_TYPE *
     52 sock_reg_update_cb (
     53   void *cls,
     54   MHD_Socket fd,
     55   enum MHD_FdState watch_for,
     56   MHD_APP_SOCKET_CNTX_TYPE *app_cntx,
     57   struct MHD_EventUpdateContext *ecb_cntx)
     58 {
     59   assert (NULL == cls);
     60   /* Note: This code only works on UNIX where MHD_Socket is an "int". */
     61   if (fd >= FD_SETSIZE)
     62     return NULL; /* not allowed by select() */
     63   if (MHD_FD_STATE_NONE == watch_for)
     64   {
     65     /* Remove from DLL */
     66     if (app_cntx == head)
     67       head = app_cntx->next;
     68     if (NULL != app_cntx->prev)
     69       app_cntx->prev->next = app_cntx->next;
     70     if (NULL != app_cntx->next)
     71       app_cntx->next->prev = app_cntx->prev;
     72     free (app_cntx);
     73     return NULL;
     74   }
     75   if (NULL == app_cntx)
     76   {
     77     /* First time, allocate data structure to keep
     78        the socket and MHD's context */
     79     app_cntx = malloc (sizeof (MHD_APP_SOCKET_CNTX_TYPE));
     80     if (NULL == app_cntx)
     81       return NULL; /* closes connection */
     82     /* prepend to DLL */
     83     app_cntx->prev = NULL;
     84     app_cntx->next = head;
     85     if (NULL != head)
     86       head->prev = app_cntx;
     87     head = app_cntx;
     88     app_cntx->fd = fd;
     89   }
     90   else
     91   {
     92     /* socket must not change */
     93     assert (fd == app_cntx->fd);
     94   }
     95   /* MHD could change its associated context, so always update */
     96   app_cntx->ecb_cntx = ecb_cntx;
     97   /* Since we are level-triggered and thus called by MHD in every
     98      iteration, we simply build the event sets for select()
     99      here directly. */
    100   if (watch_for & MHD_FD_STATE_RECV)
    101     FD_SET (fd,
    102             &rs);
    103   if (watch_for & MHD_FD_STATE_SEND)
    104     FD_SET (fd,
    105             &ws);
    106   if (watch_for & MHD_FD_STATE_EXCEPT)
    107     FD_SET (fd,
    108             &es);
    109   if (fd > max_fd)
    110     max_fd = fd;
    111   return app_cntx;
    112 }
    113 
    114 
    115 /**
    116  * We "handle" request by just closing the connection.
    117  */
    118 static struct MHD_Action *
    119 handle_request (void *cls,
    120                 struct MHD_Request *request,
    121                 const struct MHD_String *path,
    122                 enum MHD_HTTP_Method method,
    123                 uint_fast64_t upload_size)
    124 {
    125   /* We passed NULL in main() for the closure */
    126   assert (NULL == cls);
    127   /* This simply closes the connection after receiving
    128      the HTTP header, never return actually returning
    129      any data. */
    130   return MHD_action_abort_request (request);
    131 }
    132 
    133 
    134 int
    135 main ()
    136 {
    137   struct MHD_Daemon *d;
    138 
    139   /* Create an HTTP server and use "handle_request()" to
    140      handle all requests. */
    141   d = MHD_daemon_create (&handle_request,
    142                          NULL);
    143   if (MHD_SC_OK !=
    144       MHD_daemon_set_options (daemon,
    145                               MHD_D_OPTION_REREGISTER_ALL (MHD_YES),
    146                               MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_LEVEL (
    147                                 &sock_reg_update_cb,
    148                                 NULL)))
    149   {
    150     /* Maybe external event loop type not supported by build? */
    151     MHD_daemon_destroy (d);
    152     return 1;
    153   }
    154   /* We run with everything on default, so port 80, no TLS */
    155   MHD_daemon_start (d);
    156   while (1)
    157   {
    158     struct timeval ts;
    159     struct AppSockContext *pos;
    160     uint_fast64_t next_wait;
    161 
    162     FD_ZERO (&rs);
    163     FD_ZERO (&ws);
    164     FD_ZERO (&es);
    165     FD_SET (STDIN_FILENO,
    166             &rs);
    167     max_fd = STDIN_FILENO;
    168     /* This will cause MHD to call the #sock_reg_update_cb() */
    169     MHD_daemon_process_reg_events (daemon,
    170                                    &next_wait);
    171     ts.tv_sec = next_wait / 1000000;
    172     ts.tv_usec = next_wait % 1000000;
    173     /* Real applications may do nicer error handling here */
    174     (void) select (max_fd + 1,
    175                    &rs,
    176                    &ws,
    177                    &es,
    178                    &ts);
    179     if (FD_ISSET (STDIN_FILENO,
    180                   &rs))
    181       break; /* exit on input on stdin */
    182 
    183     /* Now we need to tell MHD which events were triggered */
    184     for (pos = head; NULL != pos; pos = pos->next)
    185     {
    186       enum MHD_FdState current_state = MHD_FD_STATE_NONE;
    187 
    188       if (FD_ISSET (pos->fd,
    189                     &rs))
    190         current_state |= MHD_FD_STATE_READ;
    191       if (FD_ISSET (pos->fd,
    192                     &ws))
    193         current_state |= MHD_FD_STATE_SEND;
    194       if (FD_ISSET (pos->fd,
    195                     &es))
    196         current_state |= MHD_FD_STATE_EXCEPT;
    197       if (MHD_FD_STATE_NONE == current_state)
    198         continue; /* not triggered */
    199       MHD_daemon_event_update (daemon,
    200                                pos->ecb_cntx,
    201                                current_state);
    202     }
    203   }
    204   /* Finally, just shut everything down */
    205   MHD_daemon_stop (d);
    206   return 0;
    207 }