libmicrohttpd

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

post_example.c (22772B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2011 Christian Grothoff (and other contributing authors)
      4      Copyright (C) 2014-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 post_example.c
     22  * @brief example for processing POST requests using libmicrohttpd
     23  * @author Christian Grothoff
     24  * @author Karlson2k (Evgeny Grin)
     25  */
     26 
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <stdio.h>
     30 #include <errno.h>
     31 #include <time.h>
     32 #include <microhttpd.h>
     33 
     34 /**
     35  * Invalid method page.
     36  */
     37 #define METHOD_ERROR \
     38   "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
     39 
     40 /**
     41  * Invalid URL page.
     42  */
     43 #define NOT_FOUND_ERROR \
     44   "<html><head><title>Not found</title></head><body>Go away.</body></html>"
     45 
     46 /**
     47  * Front page. (/)
     48  */
     49 #define MAIN_PAGE \
     50   "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></form></body></html>"
     51 
     52 /**
     53  * Second page. (/2)
     54  */
     55 #define SECOND_PAGE \
     56   "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></form></body></html>"
     57 
     58 /**
     59  * Second page (/S)
     60  */
     61 #define SUBMIT_PAGE \
     62   "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></form></body></html>"
     63 
     64 /**
     65  * Last page.
     66  */
     67 #define LAST_PAGE \
     68   "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
     69 
     70 /**
     71  * Name of our cookie.
     72  */
     73 #define COOKIE_NAME "session"
     74 
     75 
     76 /**
     77  * State we keep for each user/session/browser.
     78  */
     79 struct Session
     80 {
     81   /**
     82    * We keep all sessions in a linked list.
     83    */
     84   struct Session *next;
     85 
     86   /**
     87    * Unique ID for this session.
     88    */
     89   char sid[33];
     90 
     91   /**
     92    * Reference counter giving the number of connections
     93    * currently using this session.
     94    */
     95   unsigned int rc;
     96 
     97   /**
     98    * Time when this session was last active.
     99    */
    100   time_t start;
    101 
    102   /**
    103    * String submitted via form.
    104    */
    105   char value_1[64];
    106 
    107   /**
    108    * Another value submitted via form.
    109    */
    110   char value_2[64];
    111 
    112 };
    113 
    114 
    115 /**
    116  * Data kept per request.
    117  */
    118 struct Request
    119 {
    120 
    121   /**
    122    * Associated session.
    123    */
    124   struct Session *session;
    125 
    126   /**
    127    * Post processor handling form data (IF this is
    128    * a POST request).
    129    */
    130   struct MHD_PostProcessor *pp;
    131 
    132   /**
    133    * URL to serve in response to this POST (if this request
    134    * was a 'POST')
    135    */
    136   const char *post_url;
    137 
    138 };
    139 
    140 
    141 /**
    142  * Linked list of all active sessions.  Yes, O(n) but a
    143  * hash table would be overkill for a simple example...
    144  */
    145 static struct Session *sessions;
    146 
    147 
    148 /**
    149  * Return the session handle for this connection, or
    150  * create one if this is a new user.
    151  */
    152 static struct Session *
    153 get_session (struct MHD_Connection *connection)
    154 {
    155   struct Session *ret;
    156   const char *cookie;
    157 
    158   cookie = MHD_lookup_connection_value (connection,
    159                                         MHD_COOKIE_KIND,
    160                                         COOKIE_NAME);
    161   if (cookie != NULL)
    162   {
    163     /* find existing session */
    164     ret = sessions;
    165     while (NULL != ret)
    166     {
    167       if (0 == strcmp (cookie, ret->sid))
    168         break;
    169       ret = ret->next;
    170     }
    171     if (NULL != ret)
    172     {
    173       ret->rc++;
    174       return ret;
    175     }
    176   }
    177   /* create fresh session */
    178   ret = calloc (1, sizeof (struct Session));
    179   if (NULL == ret)
    180   {
    181     fprintf (stderr, "calloc error: %s\n", strerror (errno));
    182     return NULL;
    183   }
    184   /* not a super-secure way to generate a random session ID,
    185      but should do for a simple example... */
    186   snprintf (ret->sid,
    187             sizeof (ret->sid),
    188             "%X%X%X%X",
    189             (unsigned int) rand (),
    190             (unsigned int) rand (),
    191             (unsigned int) rand (),
    192             (unsigned int) rand ());
    193   ret->rc++;
    194   ret->start = time (NULL);
    195   ret->next = sessions;
    196   sessions = ret;
    197   return ret;
    198 }
    199 
    200 
    201 /**
    202  * Type of handler that generates a reply.
    203  *
    204  * @param cls content for the page (handler-specific)
    205  * @param mime mime type to use
    206  * @param session session information
    207  * @param connection connection to process
    208  * @param #MHD_YES on success, #MHD_NO on failure
    209  */
    210 typedef enum MHD_Result
    211 (*PageHandler)(const void *cls,
    212                const char *mime,
    213                struct Session *session,
    214                struct MHD_Connection *connection);
    215 
    216 
    217 /**
    218  * Entry we generate for each page served.
    219  */
    220 struct Page
    221 {
    222   /**
    223    * Acceptable URL for this page.
    224    */
    225   const char *url;
    226 
    227   /**
    228    * Mime type to set for the page.
    229    */
    230   const char *mime;
    231 
    232   /**
    233    * Handler to call to generate response.
    234    */
    235   PageHandler handler;
    236 
    237   /**
    238    * Extra argument to handler.
    239    */
    240   const void *handler_cls;
    241 };
    242 
    243 
    244 /**
    245  * Add header to response to set a session cookie.
    246  *
    247  * @param session session to use
    248  * @param response response to modify
    249  */
    250 static void
    251 add_session_cookie (struct Session *session,
    252                     struct MHD_Response *response)
    253 {
    254   char cstr[256];
    255   snprintf (cstr,
    256             sizeof (cstr),
    257             "%s=%s",
    258             COOKIE_NAME,
    259             session->sid);
    260   if (MHD_NO ==
    261       MHD_add_response_header (response,
    262                                MHD_HTTP_HEADER_SET_COOKIE,
    263                                cstr))
    264   {
    265     fprintf (stderr,
    266              "Failed to set session cookie header!\n");
    267   }
    268 }
    269 
    270 
    271 /**
    272  * Handler that returns a simple static HTTP page that
    273  * is passed in via 'cls'.
    274  *
    275  * @param cls a 'const char *' with the HTML webpage to return
    276  * @param mime mime type to use
    277  * @param session session handle
    278  * @param connection connection to use
    279  */
    280 static enum MHD_Result
    281 serve_simple_form (const void *cls,
    282                    const char *mime,
    283                    struct Session *session,
    284                    struct MHD_Connection *connection)
    285 {
    286   enum MHD_Result ret;
    287   const char *form = cls;
    288   struct MHD_Response *response;
    289 
    290   /* return static form */
    291   response = MHD_create_response_from_buffer_static (strlen (form),
    292                                                      (const void *) form);
    293   if (NULL == response)
    294     return MHD_NO;
    295   add_session_cookie (session, response);
    296   if (MHD_YES !=
    297       MHD_add_response_header (response,
    298                                MHD_HTTP_HEADER_CONTENT_ENCODING,
    299                                mime))
    300   {
    301     fprintf (stderr,
    302              "Failed to set content encoding header!\n");
    303   }
    304   ret = MHD_queue_response (connection,
    305                             MHD_HTTP_OK,
    306                             response);
    307   MHD_destroy_response (response);
    308   return ret;
    309 }
    310 
    311 
    312 /**
    313  * Handler that adds the 'v1' value to the given HTML code.
    314  *
    315  * @param cls unused
    316  * @param mime mime type to use
    317  * @param session session handle
    318  * @param connection connection to use
    319  */
    320 static enum MHD_Result
    321 fill_v1_form (const void *cls,
    322               const char *mime,
    323               struct Session *session,
    324               struct MHD_Connection *connection)
    325 {
    326   enum MHD_Result ret;
    327   size_t slen;
    328   char *reply;
    329   struct MHD_Response *response;
    330   (void) cls; /* Unused. Silent compiler warning. */
    331 
    332   slen = strlen (MAIN_PAGE) + strlen (session->value_1);
    333   reply = malloc (slen + 1);
    334   if (NULL == reply)
    335     return MHD_NO;
    336   snprintf (reply,
    337             slen + 1,
    338             MAIN_PAGE,
    339             session->value_1);
    340   /* return static form */
    341   response =
    342     MHD_create_response_from_buffer_with_free_callback (slen,
    343                                                         (void *) reply,
    344                                                         &free);
    345   if (NULL == response)
    346   {
    347     free (reply);
    348     return MHD_NO;
    349   }
    350   add_session_cookie (session, response);
    351   if (MHD_YES !=
    352       MHD_add_response_header (response,
    353                                MHD_HTTP_HEADER_CONTENT_ENCODING,
    354                                mime))
    355   {
    356     fprintf (stderr,
    357              "Failed to set content encoding header!\n");
    358   }
    359   ret = MHD_queue_response (connection,
    360                             MHD_HTTP_OK,
    361                             response);
    362   MHD_destroy_response (response);
    363   return ret;
    364 }
    365 
    366 
    367 /**
    368  * Handler that adds the 'v1' and 'v2' values to the given HTML code.
    369  *
    370  * @param cls unused
    371  * @param mime mime type to use
    372  * @param session session handle
    373  * @param connection connection to use
    374  */
    375 static enum MHD_Result
    376 fill_v1_v2_form (const void *cls,
    377                  const char *mime,
    378                  struct Session *session,
    379                  struct MHD_Connection *connection)
    380 {
    381   enum MHD_Result ret;
    382   char *reply;
    383   struct MHD_Response *response;
    384   size_t slen;
    385   (void) cls; /* Unused. Silent compiler warning. */
    386 
    387   slen = strlen (SECOND_PAGE) + strlen (session->value_1)
    388          + strlen (session->value_2);
    389   reply = malloc (slen + 1);
    390   if (NULL == reply)
    391     return MHD_NO;
    392   snprintf (reply,
    393             slen + 1,
    394             SECOND_PAGE,
    395             session->value_1,
    396             session->value_2);
    397   /* return static form */
    398   response =
    399     MHD_create_response_from_buffer_with_free_callback (slen,
    400                                                         (void *) reply,
    401                                                         &free);
    402   if (NULL == response)
    403   {
    404     free (reply);
    405     return MHD_NO;
    406   }
    407   add_session_cookie (session, response);
    408   if (MHD_YES !=
    409       MHD_add_response_header (response,
    410                                MHD_HTTP_HEADER_CONTENT_ENCODING,
    411                                mime))
    412   {
    413     fprintf (stderr,
    414              "Failed to set content encoding header!\n");
    415   }
    416   ret = MHD_queue_response (connection,
    417                             MHD_HTTP_OK,
    418                             response);
    419   MHD_destroy_response (response);
    420   return ret;
    421 }
    422 
    423 
    424 /**
    425  * Handler used to generate a 404 reply.
    426  *
    427  * @param cls a 'const char *' with the HTML webpage to return
    428  * @param mime mime type to use
    429  * @param session session handle
    430  * @param connection connection to use
    431  */
    432 static enum MHD_Result
    433 not_found_page (const void *cls,
    434                 const char *mime,
    435                 struct Session *session,
    436                 struct MHD_Connection *connection)
    437 {
    438   enum MHD_Result ret;
    439   struct MHD_Response *response;
    440   (void) cls;     /* Unused. Silent compiler warning. */
    441   (void) session; /* Unused. Silent compiler warning. */
    442 
    443   /* unsupported HTTP method */
    444   response =
    445     MHD_create_response_from_buffer_static (strlen (NOT_FOUND_ERROR),
    446                                             (const void *) NOT_FOUND_ERROR);
    447   if (NULL == response)
    448     return MHD_NO;
    449   ret = MHD_queue_response (connection,
    450                             MHD_HTTP_NOT_FOUND,
    451                             response);
    452   if (MHD_YES !=
    453       MHD_add_response_header (response,
    454                                MHD_HTTP_HEADER_CONTENT_ENCODING,
    455                                mime))
    456   {
    457     fprintf (stderr,
    458              "Failed to set content encoding header!\n");
    459   }
    460   MHD_destroy_response (response);
    461   return ret;
    462 }
    463 
    464 
    465 /**
    466  * List of all pages served by this HTTP server.
    467  */
    468 static struct Page pages[] = {
    469   { "/", "text/html",  &fill_v1_form, NULL },
    470   { "/2", "text/html", &fill_v1_v2_form, NULL },
    471   { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
    472   { "/F", "text/html", &serve_simple_form, LAST_PAGE },
    473   { NULL, NULL, &not_found_page, NULL }   /* 404 */
    474 };
    475 
    476 
    477 /**
    478  * Iterator over key-value pairs where the value
    479  * maybe made available in increments and/or may
    480  * not be zero-terminated.  Used for processing
    481  * POST data.
    482  *
    483  * @param cls user-specified closure
    484  * @param kind type of the value
    485  * @param key 0-terminated key for the value
    486  * @param filename name of the uploaded file, NULL if not known
    487  * @param content_type mime-type of the data, NULL if not known
    488  * @param transfer_encoding encoding of the data, NULL if not known
    489  * @param data pointer to size bytes of data at the
    490  *              specified offset
    491  * @param off offset of data in the overall value
    492  * @param size number of bytes in data available
    493  * @return MHD_YES to continue iterating,
    494  *         MHD_NO to abort the iteration
    495  */
    496 static enum MHD_Result
    497 post_iterator (void *cls,
    498                enum MHD_ValueKind kind,
    499                const char *key,
    500                const char *filename,
    501                const char *content_type,
    502                const char *transfer_encoding,
    503                const char *data, uint64_t off, size_t size)
    504 {
    505   struct Request *request = cls;
    506   struct Session *session = request->session;
    507   (void) kind;              /* Unused. Silent compiler warning. */
    508   (void) filename;          /* Unused. Silent compiler warning. */
    509   (void) content_type;      /* Unused. Silent compiler warning. */
    510   (void) transfer_encoding; /* Unused. Silent compiler warning. */
    511 
    512   if (0 == strcmp ("DONE", key))
    513   {
    514     fprintf (stdout,
    515              "Session `%s' submitted `%s', `%s'\n",
    516              session->sid,
    517              session->value_1,
    518              session->value_2);
    519     return MHD_YES;
    520   }
    521   if (0 == strcmp ("v1", key))
    522   {
    523     if (off >= sizeof(session->value_1) - 1)
    524       return MHD_YES; /* Discard extra data */
    525     if (size + off >= sizeof(session->value_1))
    526       size = (size_t) (sizeof (session->value_1) - off - 1); /* crop extra data */
    527     memcpy (&session->value_1[off],
    528             data,
    529             size);
    530     session->value_1[size + off] = '\0';
    531     return MHD_YES;
    532   }
    533   if (0 == strcmp ("v2", key))
    534   {
    535     if (off >= sizeof(session->value_2) - 1)
    536       return MHD_YES; /* Discard extra data */
    537     if (size + off >= sizeof(session->value_2))
    538       size = (size_t) (sizeof (session->value_2) - off - 1); /* crop extra data */
    539     memcpy (&session->value_2[off],
    540             data,
    541             size);
    542     session->value_2[size + off] = '\0';
    543     return MHD_YES;
    544   }
    545   fprintf (stderr,
    546            "Unsupported form value `%s'\n",
    547            key);
    548   return MHD_YES;
    549 }
    550 
    551 
    552 /**
    553  * Main MHD callback for handling requests.
    554  *
    555  * @param cls argument given together with the function
    556  *        pointer when the handler was registered with MHD
    557  * @param connection handle identifying the incoming connection
    558  * @param url the requested url
    559  * @param method the HTTP method used ("GET", "PUT", etc.)
    560  * @param version the HTTP version string (i.e. "HTTP/1.1")
    561  * @param upload_data the data being uploaded (excluding HEADERS,
    562  *        for a POST that fits into memory and that is encoded
    563  *        with a supported encoding, the POST data will NOT be
    564  *        given in upload_data and is instead available as
    565  *        part of MHD_get_connection_values; very large POST
    566  *        data *will* be made available incrementally in
    567  *        upload_data)
    568  * @param upload_data_size set initially to the size of the
    569  *        upload_data provided; the method must update this
    570  *        value to the number of bytes NOT processed;
    571  * @param req_cls pointer that the callback can set to some
    572  *        address and that will be preserved by MHD for future
    573  *        calls for this request; since the access handler may
    574  *        be called many times (i.e., for a PUT/POST operation
    575  *        with plenty of upload data) this allows the application
    576  *        to easily associate some request-specific state.
    577  *        If necessary, this state can be cleaned up in the
    578  *        global "MHD_RequestCompleted" callback (which
    579  *        can be set with the MHD_OPTION_NOTIFY_COMPLETED).
    580  *        Initially, <tt>*req_cls</tt> will be NULL.
    581  * @return MHS_YES if the connection was handled successfully,
    582  *         MHS_NO if the socket must be closed due to a serious
    583  *         error while handling the request
    584  */
    585 static enum MHD_Result
    586 create_response (void *cls,
    587                  struct MHD_Connection *connection,
    588                  const char *url,
    589                  const char *method,
    590                  const char *version,
    591                  const char *upload_data,
    592                  size_t *upload_data_size,
    593                  void **req_cls)
    594 {
    595   struct MHD_Response *response;
    596   struct Request *request;
    597   struct Session *session;
    598   enum MHD_Result ret;
    599   unsigned int i;
    600   (void) cls;               /* Unused. Silent compiler warning. */
    601   (void) version;           /* Unused. Silent compiler warning. */
    602 
    603   request = *req_cls;
    604   if (NULL == request)
    605   {
    606     request = calloc (1, sizeof (struct Request));
    607     if (NULL == request)
    608     {
    609       fprintf (stderr, "calloc error: %s\n", strerror (errno));
    610       return MHD_NO;
    611     }
    612     *req_cls = request;
    613     if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    614     {
    615       request->pp = MHD_create_post_processor (connection, 1024,
    616                                                &post_iterator, request);
    617       if (NULL == request->pp)
    618       {
    619         fprintf (stderr, "Failed to setup post processor for `%s'\n",
    620                  url);
    621         return MHD_NO; /* internal error */
    622       }
    623     }
    624     return MHD_YES;
    625   }
    626   if (NULL == request->session)
    627   {
    628     request->session = get_session (connection);
    629     if (NULL == request->session)
    630     {
    631       fprintf (stderr, "Failed to setup session for `%s'\n",
    632                url);
    633       return MHD_NO; /* internal error */
    634     }
    635   }
    636   session = request->session;
    637   session->start = time (NULL);
    638   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    639   {
    640     /* evaluate POST data */
    641     if (MHD_YES !=
    642         MHD_post_process (request->pp,
    643                           upload_data,
    644                           *upload_data_size))
    645       return MHD_NO;
    646     if (0 != *upload_data_size)
    647     {
    648       *upload_data_size = 0;
    649       return MHD_YES;
    650     }
    651     /* done with POST data, serve response */
    652     MHD_destroy_post_processor (request->pp);
    653     request->pp = NULL;
    654     method = MHD_HTTP_METHOD_GET;   /* fake 'GET' */
    655     if (NULL != request->post_url)
    656       url = request->post_url;
    657   }
    658 
    659   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
    660        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
    661   {
    662     /* find out which page to serve */
    663     i = 0;
    664     while ( (pages[i].url != NULL) &&
    665             (0 != strcmp (pages[i].url, url)) )
    666       i++;
    667     ret = pages[i].handler (pages[i].handler_cls,
    668                             pages[i].mime,
    669                             session, connection);
    670     if (ret != MHD_YES)
    671       fprintf (stderr, "Failed to create page for `%s'\n",
    672                url);
    673     return ret;
    674   }
    675   /* unsupported HTTP method */
    676   response =
    677     MHD_create_response_from_buffer_static (strlen (METHOD_ERROR),
    678                                             (const void *) METHOD_ERROR);
    679   ret = MHD_queue_response (connection,
    680                             MHD_HTTP_NOT_ACCEPTABLE,
    681                             response);
    682   MHD_destroy_response (response);
    683   return ret;
    684 }
    685 
    686 
    687 /**
    688  * Callback called upon completion of a request.
    689  * Decrements session reference counter.
    690  *
    691  * @param cls not used
    692  * @param connection connection that completed
    693  * @param req_cls session handle
    694  * @param toe status code
    695  */
    696 static void
    697 request_completed_callback (void *cls,
    698                             struct MHD_Connection *connection,
    699                             void **req_cls,
    700                             enum MHD_RequestTerminationCode toe)
    701 {
    702   struct Request *request = *req_cls;
    703   (void) cls;         /* Unused. Silent compiler warning. */
    704   (void) connection;  /* Unused. Silent compiler warning. */
    705   (void) toe;         /* Unused. Silent compiler warning. */
    706 
    707   if (NULL == request)
    708     return;
    709   if (NULL != request->session)
    710     request->session->rc--;
    711   if (NULL != request->pp)
    712     MHD_destroy_post_processor (request->pp);
    713   free (request);
    714 }
    715 
    716 
    717 /**
    718  * Clean up handles of sessions that have been idle for
    719  * too long.
    720  */
    721 static void
    722 expire_sessions (void)
    723 {
    724   struct Session *pos;
    725   struct Session *prev;
    726   struct Session *next;
    727   time_t now;
    728 
    729   now = time (NULL);
    730   prev = NULL;
    731   pos = sessions;
    732   while (NULL != pos)
    733   {
    734     next = pos->next;
    735     if (now - pos->start > 60 * 60)
    736     {
    737       /* expire sessions after 1h */
    738       if (NULL == prev)
    739         sessions = pos->next;
    740       else
    741         prev->next = next;
    742       free (pos);
    743     }
    744     else
    745       prev = pos;
    746     pos = next;
    747   }
    748 }
    749 
    750 
    751 /**
    752  * Call with the port number as the only argument.
    753  * Never terminates (other than by signals, such as CTRL-C).
    754  */
    755 int
    756 main (int argc, char *const *argv)
    757 {
    758   struct MHD_Daemon *d;
    759   struct timeval tv;
    760   struct timeval *tvp;
    761   fd_set rs;
    762   fd_set ws;
    763   fd_set es;
    764   MHD_socket max;
    765   uint64_t mhd_timeout;
    766   int port;
    767 
    768   if (argc != 2)
    769   {
    770     printf ("%s PORT\n", argv[0]);
    771     return 1;
    772   }
    773   port = atoi (argv[1]);
    774   if ( (1 > port) || (port > 65535) )
    775   {
    776     fprintf (stderr,
    777              "Port must be a number between 1 and 65535.\n");
    778     return 1;
    779   }
    780   /* initialize PRNG */
    781   srand ((unsigned int) time (NULL));
    782   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    783                         (uint16_t) port,
    784                         NULL, NULL,
    785                         &create_response, NULL,
    786                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
    787                         MHD_OPTION_NOTIFY_COMPLETED,
    788                         &request_completed_callback, NULL,
    789                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    790                         MHD_OPTION_END);
    791   if (NULL == d)
    792     return 1;
    793   while (1)
    794   {
    795     expire_sessions ();
    796     max = 0;
    797     FD_ZERO (&rs);
    798     FD_ZERO (&ws);
    799     FD_ZERO (&es);
    800     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    801       break; /* fatal internal error */
    802     if (MHD_get_timeout64 (d, &mhd_timeout) == MHD_YES)
    803     {
    804 #if ! defined(_WIN32) || defined(__CYGWIN__)
    805       tv.tv_sec = (time_t) (mhd_timeout / 1000LL);
    806 #else  /* Native W32 */
    807       tv.tv_sec = (long) (mhd_timeout / 1000LL);
    808 #endif /* Native W32 */
    809       tv.tv_usec = ((long) (mhd_timeout % 1000)) * 1000;
    810       tvp = &tv;
    811     }
    812     else
    813       tvp = NULL;
    814     if (-1 == select ((int) max + 1, &rs, &ws, &es, tvp))
    815     {
    816       if (EINTR != errno)
    817         abort ();
    818     }
    819     MHD_run (d);
    820   }
    821   MHD_stop_daemon (d);
    822   return 0;
    823 }