libmicrohttpd

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

sessions.c (22950B)


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