libmicrohttpd

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

suspend_resume_epoll.c (5701B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2018 Christian Grothoff (and other contributing authors)
      4      Copyright (C) 2022 Evgeny Grin (Karlson2k)
      5 
      6      This library is free software; you can redistribute it and/or
      7      modify it under the terms of the GNU Lesser General Public
      8      License as published by the Free Software Foundation; either
      9      version 2.1 of the License, or (at your option) any later version.
     10 
     11      This library is distributed in the hope that it will be useful,
     12      but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      Lesser General Public License for more details.
     15 
     16      You should have received a copy of the GNU Lesser General Public
     17      License along with this library; if not, write to the Free Software
     18      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19 */
     20 /**
     21  * @file suspend_resume_epoll.c
     22  * @brief example for how to use libmicrohttpd with epoll() and
     23  *        resume a suspended connection
     24  * @author Robert D Kocisko
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 #include "platform.h"
     29 #include <microhttpd.h>
     30 #include <sys/epoll.h>
     31 #include <sys/timerfd.h>
     32 #include <limits.h>
     33 #include <errno.h>
     34 
     35 #define TIMEOUT_INFINITE -1
     36 
     37 struct Request
     38 {
     39   struct MHD_Connection *connection;
     40   int timerfd;
     41 };
     42 
     43 
     44 static int epfd;
     45 
     46 static struct epoll_event evt;
     47 
     48 
     49 static enum MHD_Result
     50 ahc_echo (void *cls,
     51           struct MHD_Connection *connection,
     52           const char *url,
     53           const char *method,
     54           const char *version,
     55           const char *upload_data, size_t *upload_data_size, void **req_cls)
     56 {
     57   struct MHD_Response *response;
     58   enum MHD_Result ret;
     59   struct Request *req;
     60   struct itimerspec ts;
     61 
     62   (void) cls;
     63   (void) method;
     64   (void) version;           /* Unused. Silence compiler warning. */
     65   (void) upload_data;       /* Unused. Silence compiler warning. */
     66   (void) upload_data_size;  /* Unused. Silence compiler warning. */
     67   req = *req_cls;
     68   if (NULL == req)
     69   {
     70 
     71     req = malloc (sizeof(struct Request));
     72     if (NULL == req)
     73       return MHD_NO;
     74     req->connection = connection;
     75     req->timerfd = -1;
     76     *req_cls = req;
     77     return MHD_YES;
     78   }
     79 
     80   if (-1 != req->timerfd)
     81   {
     82     /* send response (echo request url) */
     83     response = MHD_create_response_from_buffer_copy (strlen (url),
     84                                                      (const void *) url);
     85     if (NULL == response)
     86       return MHD_NO;
     87     ret = MHD_queue_response (connection,
     88                               MHD_HTTP_OK,
     89                               response);
     90     MHD_destroy_response (response);
     91     return ret;
     92   }
     93   /* create timer and suspend connection */
     94   req->timerfd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK);
     95   if (-1 == req->timerfd)
     96   {
     97     printf ("timerfd_create: %s", strerror (errno));
     98     return MHD_NO;
     99   }
    100   evt.events = EPOLLIN;
    101   evt.data.ptr = req;
    102   if (-1 == epoll_ctl (epfd, EPOLL_CTL_ADD, req->timerfd, &evt))
    103   {
    104     printf ("epoll_ctl: %s", strerror (errno));
    105     return MHD_NO;
    106   }
    107   ts.it_value.tv_sec = 1;
    108   ts.it_value.tv_nsec = 0;
    109   ts.it_interval.tv_sec = 0;
    110   ts.it_interval.tv_nsec = 0;
    111   if (-1 == timerfd_settime (req->timerfd, 0, &ts, NULL))
    112   {
    113     printf ("timerfd_settime: %s", strerror (errno));
    114     return MHD_NO;
    115   }
    116   MHD_suspend_connection (connection);
    117   return MHD_YES;
    118 }
    119 
    120 
    121 static void
    122 connection_done (void *cls,
    123                  struct MHD_Connection *connection,
    124                  void **req_cls,
    125                  enum MHD_RequestTerminationCode toe)
    126 {
    127   struct Request *req = *req_cls;
    128 
    129   (void) cls;
    130   (void) connection;
    131   (void) toe;
    132   if (-1 != req->timerfd)
    133     if (0 != close (req->timerfd))
    134       abort ();
    135   free (req);
    136 }
    137 
    138 
    139 int
    140 main (int argc,
    141       char *const *argv)
    142 {
    143   struct MHD_Daemon *d;
    144   const union MHD_DaemonInfo *info;
    145   int current_event_count;
    146   struct epoll_event events_list[1];
    147   struct Request *req;
    148   uint64_t timer_expirations;
    149   int port;
    150 
    151   if (argc != 2)
    152   {
    153     printf ("%s PORT\n", argv[0]);
    154     return 1;
    155   }
    156   port = atoi (argv[1]);
    157   if ( (1 > port) || (port > 65535) )
    158   {
    159     fprintf (stderr,
    160              "Port must be a number between 1 and 65535.\n");
    161     return 1;
    162   }
    163   d = MHD_start_daemon (MHD_USE_EPOLL | MHD_ALLOW_SUSPEND_RESUME,
    164                         (uint16_t) port,
    165                         NULL, NULL, &ahc_echo, NULL,
    166                         MHD_OPTION_NOTIFY_COMPLETED, &connection_done, NULL,
    167                         MHD_OPTION_END);
    168   if (d == NULL)
    169     return 1;
    170 
    171   info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_EPOLL_FD);
    172   if (info == NULL)
    173     return 1;
    174 
    175   epfd = epoll_create1 (EPOLL_CLOEXEC);
    176   if (-1 == epfd)
    177     return 1;
    178 
    179   evt.events = EPOLLIN;
    180   evt.data.ptr = NULL;
    181   if (-1 == epoll_ctl (epfd, EPOLL_CTL_ADD, info->epoll_fd, &evt))
    182     return 1;
    183 
    184   while (1)
    185   {
    186     current_event_count = epoll_wait (epfd, events_list, 1,
    187                                       MHD_get_timeout_i (d));
    188 
    189     if (1 == current_event_count)
    190     {
    191       if (events_list[0].data.ptr)
    192       {
    193         /*  A timer has timed out */
    194         req = events_list[0].data.ptr;
    195         /* read from the fd so the system knows we heard the notice */
    196         if (-1 == read (req->timerfd, &timer_expirations,
    197                         sizeof(timer_expirations)))
    198         {
    199           return 1;
    200         }
    201         /*  Now resume the connection */
    202         MHD_resume_connection (req->connection);
    203       }
    204     }
    205     else if (0 == current_event_count)
    206     {
    207       /* no events: continue */
    208     }
    209     else
    210     {
    211       /* error */
    212       return 1;
    213     }
    214     if (! MHD_run (d))
    215       return 1;
    216   }
    217 
    218   return 0;
    219 }