libmicrohttpd2

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

demo.c (40968B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2013-2024 Christian Grothoff (and other contributing authors)
      5   Copyright (C) 2014-2025 Evgeny Grin (Karlson2k)
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 /**
     40  * @file demo.c
     41  * @brief complex demonstration site: create directory index, offer
     42  *        upload via form and HTTP POST, download with mime type detection
     43  *        and error reporting (403, etc.) --- and all of this with
     44  *        high-performance settings (large buffers, thread pool).
     45  *        If you want to benchmark MHD, this code should be used to
     46  *        run tests against.  Note that the number of threads may need
     47  *        to be adjusted depending on the number of available cores.
     48  * @author Christian Grothoff
     49  * @author Karlson2k (Evgeny Grin)
     50  */
     51 #include <microhttpd2.h>
     52 #include <sys/types.h>
     53 #include <stdbool.h>
     54 #if ! defined(_WIN32) || defined(__CYGWIN__)
     55 #  include <unistd.h>
     56 #else
     57 #  include <direct.h>
     58 #endif
     59 #include <stdio.h>
     60 #include <stdlib.h>
     61 #include <signal.h>
     62 #include <fcntl.h>
     63 #include <sys/stat.h>
     64 #include <string.h>
     65 #if ! defined(_WIN32) || defined(__CYGWIN__)
     66 #  include <dirent.h>
     67 #else
     68 #  include <io.h>
     69 #endif
     70 #ifdef MHD_HAVE_LIBMAGIC
     71 #  include <magic.h>
     72 #endif /* MHD_HAVE_LIBMAGIC */
     73 #include <limits.h>
     74 #include <ctype.h>
     75 #include <errno.h>
     76 #if defined(_WIN32) && ! defined(__CYGWIN__)
     77 #  include <locale.h>
     78 #endif
     79 
     80 #if ! defined(_WIN32) || defined(__CYGWIN__)
     81 #  include <pthread.h>
     82 typedef pthread_mutex_t my_mutex_t;
     83 #  define my_mutex_init(pmtx) ((void) pthread_mutex_init ((pmtx), NULL))
     84 #  define my_mutex_destroy(pmtx) ((void) pthread_mutex_destroy ((pmtx)))
     85 #  define my_mutex_lock(pmtx) ((void) pthread_mutex_lock ((pmtx)))
     86 #  define my_mutex_unlock(pmtx) ((void) pthread_mutex_unlock ((pmtx)))
     87 #else
     88 #  define WIN32_LEAN_AND_MEAN 1
     89 #  include <windows.h>
     90 typedef CRITICAL_SECTION my_mutex_t;
     91 #  define my_mutex_init(pmtx) ((void) InitializeCriticalSection ((pmtx)))
     92 #  define my_mutex_destroy(pmtx) DeleteCriticalSection ((pmtx))
     93 #  define my_mutex_lock(pmtx) EnterCriticalSection ((pmtx))
     94 #  define my_mutex_unlock(pmtx) LeaveCriticalSection ((pmtx))
     95 #endif
     96 
     97 #ifdef S_ISREG
     98 #  define my_S_ISREG(arg) S_ISREG (arg)
     99 #else
    100 #  define my_S_ISREG(arg) (_S_IFREG == ((arg)&_S_IFMT))
    101 #endif
    102 
    103 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
    104 #undef MHD_CPU_COUNT
    105 #endif
    106 #if ! defined(MHD_CPU_COUNT)
    107 #define MHD_CPU_COUNT 2
    108 #endif
    109 
    110 #ifndef PATH_MAX
    111 #  ifdef MAX_PATH
    112 #    define PATH_MAX MAX_PATH
    113 #  else
    114 /* Some platforms (namely: GNU Hurd) do no define PATH_MAX.
    115    As it is only example for MHD, just use reasonable value for PATH_MAX. */
    116 #    define PATH_MAX 16384
    117 #  endif
    118 #endif
    119 
    120 /**
    121  * Number of threads to run in the thread pool.  Should (roughly) match
    122  * the number of cores on your system.
    123  */
    124 #define NUMBER_OF_THREADS MHD_CPU_COUNT
    125 
    126 #ifdef MHD_HAVE_LIBMAGIC
    127 /**
    128  * How many bytes of a file do we give to libmagic to determine the mime type?
    129  * 16k might be a bit excessive, but ought not hurt performance much anyway,
    130  * and should definitively be on the safe side.
    131  */
    132 #define MAGIC_HEADER_SIZE (16 * 1024)
    133 #endif /* MHD_HAVE_LIBMAGIC */
    134 
    135 #if defined(_MSC_VER) && _MSC_VER < 1900 /* Before VS 2015 */
    136 /* _snprintf works differently, but the code handles it correctly */
    137 #  define snprintf _snprintf
    138 #endif
    139 
    140 #if ! defined(_WIN32) || defined(__CYGWIN__)
    141 #  define my_mkstemp(tmpl) mkstemp ((tmpl))
    142 #else
    143 static int
    144 my_mkstemp (char *tmpl)
    145 {
    146   int i;
    147 
    148   for (i = 0; i < 32; ++i)
    149   {
    150     char tmp_file_name[PATH_MAX + 1];
    151     HANDLE hndl;
    152     if (0 == GetTempFileNameA (".",
    153                                tmpl,
    154                                0,
    155                                tmp_file_name))
    156       return -1;
    157     hndl = CreateFileA (tmp_file_name,
    158                         GENERIC_READ | GENERIC_WRITE,
    159                         0,
    160                         NULL,
    161                         CREATE_ALWAYS,
    162                         FILE_FLAG_SEQUENTIAL_SCAN,
    163                         NULL);
    164     if (INVALID_HANDLE_VALUE != hndl)
    165     {
    166       int fd;
    167       fd = _open_osfhandle ((intptr_t) hndl,
    168                             _O_RDWR | _O_BINARY);
    169       if (-1 != fd)
    170       {
    171         strcpy (tmpl, tmp_file_name);
    172         return fd; /* Success */
    173       }
    174 
    175       /* Failure */
    176       CloseHandle (hndl);
    177     }
    178     (void) DeleteFileA (tmp_file_name);
    179   }
    180   return -1;
    181 }
    182 
    183 
    184 #endif
    185 
    186 #ifdef HAVE_STRDUP
    187 #  define my_strdup(str) strdup ((str))
    188 #else
    189 static char *
    190 my_strdup (const char *str)
    191 {
    192   size_t sz;
    193   char *str_copy;
    194 
    195   sz = strlen (str);
    196   str_copy = (char *) malloc (sz);
    197   if (NULL != str_copy)
    198     memcpy (str_copy, str, sz);
    199   return str_copy;
    200 }
    201 
    202 
    203 #endif
    204 
    205 
    206 /**
    207  * Page returned for file-not-found.
    208  */
    209 #define FILE_NOT_FOUND_PAGE \
    210         "<html><head><title>File not found</title></head><body>File not found</body></html>"
    211 
    212 
    213 /**
    214  * Page returned for internal errors.
    215  */
    216 #define INTERNAL_ERROR_PAGE \
    217         "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
    218 
    219 
    220 /**
    221  * Page returned for refused requests.
    222  */
    223 #define REQUEST_REFUSED_PAGE \
    224         "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
    225 
    226 
    227 /**
    228  * Head of index page.
    229  */
    230 #define INDEX_PAGE_HEADER \
    231         "<html>\n<head><title>Welcome</title></head>\n<body>\n" \
    232         "<h1>Upload</h1>\n" \
    233         "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n" \
    234         "<dl><dt>Content type:</dt><dd>" \
    235         "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>" \
    236         "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>" \
    237         "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>" \
    238         "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>" \
    239         "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n" \
    240         "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>" \
    241         "<dt>Language:</dt><dd>" \
    242         "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>" \
    243         "<input type=\"radio\" name=\"language\" value=\"en\">English</input>" \
    244         "<input type=\"radio\" name=\"language\" value=\"de\">German</input>" \
    245         "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>" \
    246         "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n" \
    247         "<dt>File:</dt><dd>" \
    248         "<input type=\"file\" name=\"upload\"/></dd></dl>" \
    249         "<input type=\"submit\" value=\"Send!\"/>\n" \
    250         "</form>\n" \
    251         "<h1>Download</h1>\n" \
    252         "<ol>\n"
    253 
    254 /**
    255  * Footer of index page.
    256  */
    257 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
    258 
    259 
    260 /**
    261  * NULL-terminated array of supported upload categories.  Should match HTML
    262  * in the form.
    263  */
    264 static const char *const categories[] = {
    265   "books",
    266   "images",
    267   "music",
    268   "software",
    269   "videos",
    270   "other",
    271   NULL,
    272 };
    273 
    274 
    275 /**
    276  * Specification of a supported language.
    277  */
    278 struct Language
    279 {
    280   /**
    281    * Directory name for the language.
    282    */
    283   const char *dirname;
    284 
    285   /**
    286    * Long name for humans.
    287    */
    288   const char *longname;
    289 
    290 };
    291 
    292 /**
    293  * NULL-terminated array of supported upload categories.  Should match HTML
    294  * in the form.
    295  */
    296 static const struct Language languages[] = {
    297   { "no-lang", "No language specified" },
    298   { "en", "English" },
    299   { "de", "German" },
    300   { "fr", "French" },
    301   { "es", "Spanish" },
    302   { NULL, NULL },
    303 };
    304 
    305 
    306 /**
    307  * Response returned if the requested file does not exist (or is not accessible).
    308  */
    309 static struct MHD_Response *file_not_found_response;
    310 
    311 /**
    312  * Response returned for internal errors.
    313  */
    314 static struct MHD_Response *internal_error_response;
    315 
    316 /**
    317  * Response returned for '/' (GET) to list the contents of the directory and allow upload.
    318  */
    319 static struct MHD_Response *cached_directory_response;
    320 
    321 /**
    322  * Response returned for refused uploads.
    323  */
    324 static struct MHD_Response *request_refused_response;
    325 
    326 /**
    327  * Mutex used when we update the cached directory response object.
    328  */
    329 static my_mutex_t mutex;
    330 
    331 #ifdef MHD_HAVE_LIBMAGIC
    332 /**
    333  * Global handle to MAGIC data.
    334  */
    335 static magic_t magic;
    336 #endif /* MHD_HAVE_LIBMAGIC */
    337 
    338 
    339 /**
    340  * Mark the given response as HTML for the browser.
    341  *
    342  * @param response response to mark
    343  */
    344 static void
    345 mark_as_html (struct MHD_Response *response)
    346 {
    347   if (NULL == response)
    348     return;
    349   (void) MHD_response_add_header (response,
    350                                   MHD_HTTP_HEADER_CONTENT_TYPE,
    351                                   "text/html");
    352 }
    353 
    354 
    355 /**
    356  * Replace the existing 'cached_directory_response' with the
    357  * given response.
    358  *
    359  * @param response new directory response
    360  */
    361 static void
    362 update_cached_response (struct MHD_Response *response)
    363 {
    364   if (NULL != response)
    365   {
    366     if (MHD_SC_OK !=
    367         MHD_response_set_option (response,
    368                                  &MHD_R_OPTION_REUSABLE (
    369                                    MHD_YES)))
    370       exit (1);
    371   }
    372   my_mutex_lock (&mutex);
    373   if (NULL != cached_directory_response)
    374     MHD_response_destroy (cached_directory_response);
    375   cached_directory_response = response;
    376   my_mutex_unlock (&mutex);
    377 }
    378 
    379 
    380 /**
    381  * Context keeping the data for the response we're building.
    382  */
    383 struct ResponseDataContext
    384 {
    385   /**
    386    * Response data string.
    387    */
    388   char *buf;
    389 
    390   /**
    391    * Number of bytes allocated for 'buf'.
    392    */
    393   size_t buf_size;
    394 
    395   /**
    396    * Current position where we append to 'buf'. Must be smaller or equal to 'buf_size'.
    397    */
    398   size_t off;
    399 
    400 };
    401 
    402 
    403 /**
    404  * Create a listing of the files in 'dirname' in HTML.
    405  *
    406  * @param rdc where to store the list of files
    407  * @param dirname name of the directory to list
    408  * @return true on success, false on error
    409  */
    410 static bool
    411 list_directory (struct ResponseDataContext *rdc,
    412                 const char *dirname)
    413 {
    414 #if ! defined(_WIN32) || defined(__CYGWIN__)
    415   char fullname[PATH_MAX + 1];
    416   struct stat sbuf;
    417   DIR *dir;
    418   struct dirent *de;
    419 
    420   if (NULL == (dir = opendir (dirname)))
    421     return false;
    422   while (NULL != (de = readdir (dir)))
    423   {
    424     int res;
    425     if ('.' == de->d_name[0])
    426       continue;
    427     if (sizeof (fullname) <= (unsigned int)
    428         snprintf (fullname, sizeof (fullname),
    429                   "%s/%s",
    430                   dirname, de->d_name))
    431       continue;  /* ugh, file too long? how can this be!? */
    432     if (0 != stat (fullname, &sbuf))
    433       continue;  /* ugh, failed to 'stat' */
    434     if (! S_ISREG (sbuf.st_mode))
    435       continue;  /* not a regular file, skip */
    436     if (rdc->off + 1024 > rdc->buf_size)
    437     {
    438       void *r;
    439 
    440       if ((2 * rdc->buf_size < rdc->buf_size) ||
    441           (2 * rdc->buf_size + 1024) < rdc->buf_size)
    442         break; /* more than SIZE_T _index_ size? Too big for us */
    443       rdc->buf_size = 2 * rdc->buf_size + 1024;
    444       if (NULL == (r = realloc (rdc->buf, rdc->buf_size)))
    445         break; /* out of memory */
    446       rdc->buf = (char *) r;
    447     }
    448     res = snprintf (rdc->buf + rdc->off,
    449                     rdc->buf_size - rdc->off,
    450                     "<li><a href=\"/%s\">%s</a></li>\n",
    451                     fullname,
    452                     de->d_name);
    453     if (0 >= res)
    454       continue;  /* snprintf() error */
    455     if (rdc->buf_size - rdc->off <= (size_t) res)
    456       continue;  /* buffer too small?? */
    457     rdc->off += (size_t) res;
    458   }
    459   (void) closedir (dir);
    460 #else
    461   char search_patt[PATH_MAX + 1];
    462   struct _finddatai64_t finfo;
    463   intptr_t dir;
    464   if (sizeof(search_patt) <=
    465       (size_t) snprintf (search_patt,
    466                          sizeof(search_patt),
    467                          "%s\\*",
    468                          dirname))
    469     return false;
    470   if (-1 == (dir = _findfirsti64 (search_patt,
    471                                   &finfo)))
    472     return false;
    473   do
    474   {
    475     int res;
    476 
    477     if (0 != (finfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM)))
    478       continue;  /* Not a regular file, skip */
    479     if (rdc->off + 1024 > rdc->buf_size)
    480     {
    481       void *r;
    482 
    483       if ((2 * rdc->buf_size < rdc->buf_size) ||
    484           (2 * rdc->buf_size + 1024) < rdc->buf_size)
    485         break; /* more than SIZE_T _index_ size? Too big for us */
    486       rdc->buf_size = 2 * rdc->buf_size + 1024;
    487       if (NULL == (r = realloc (rdc->buf, rdc->buf_size)))
    488         break; /* out of memory */
    489       rdc->buf = (char *) r;
    490     }
    491     res = snprintf (rdc->buf + rdc->off,
    492                     rdc->buf_size - rdc->off,
    493                     "<li><a href=\"/%s/%s\">%s</a></li>\n",
    494                     dirname,
    495                     finfo.name,
    496                     finfo.name);
    497     if (0 >= res)
    498       continue;  /* snprintf() error */
    499     if (rdc->buf_size - rdc->off <= (size_t) res)
    500       continue;  /* buffer too small?? */
    501     rdc->off += (size_t) res;
    502 
    503   } while (0 == _findnexti64 (dir,
    504                               &finfo));
    505   (void) _findclose (dir);
    506 #endif
    507   return true;
    508 }
    509 
    510 
    511 /**
    512  * Re-scan our local directory and re-build the index.
    513  */
    514 static void
    515 update_directory (void)
    516 {
    517   static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
    518   struct MHD_Response *response;
    519   struct ResponseDataContext rdc;
    520   unsigned int language_idx;
    521   unsigned int category_idx;
    522   const struct Language *language;
    523   const char *category;
    524   char dir_name[128];
    525   struct stat sbuf;
    526   int res;
    527   size_t len;
    528 
    529   rdc.off = 0;
    530   rdc.buf_size = initial_allocation;
    531   rdc.buf = (char *) malloc (rdc.buf_size);
    532   if (NULL == rdc.buf)
    533   {
    534     update_cached_response (NULL);
    535     return;
    536   }
    537   len = strlen (INDEX_PAGE_HEADER);
    538   if (rdc.buf_size < len)
    539   { /* buffer too small */
    540     free (rdc.buf);
    541     update_cached_response (NULL);
    542     return;
    543   }
    544   memcpy (rdc.buf + rdc.off,
    545           INDEX_PAGE_HEADER,
    546           len);
    547   rdc.off += len;
    548   for (language_idx = 0; NULL != languages[language_idx].dirname;
    549        language_idx++)
    550   {
    551     language = &languages[language_idx];
    552 
    553     if (0 != stat (language->dirname, &sbuf))
    554       continue; /* empty */
    555     /* we ensured always +1k room, filenames are ~256 bytes,
    556        so there is always still enough space for the header
    557        without need for an additional reallocation check. */
    558     res = snprintf (rdc.buf + rdc.off, rdc.buf_size - rdc.off,
    559                     "<h2>%s</h2>\n",
    560                     language->longname);
    561     if (0 >= res)
    562       continue;  /* snprintf() error */
    563     if (rdc.buf_size - rdc.off <= (size_t) res)
    564       continue;  /* buffer too small?? */
    565     rdc.off += (size_t) res;
    566     for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
    567     {
    568       category = categories[category_idx];
    569       res = snprintf (dir_name, sizeof (dir_name),
    570                       "%s/%s",
    571                       language->dirname,
    572                       category);
    573       if ((0 >= res) || (sizeof (dir_name) <= (size_t) res))
    574         continue;  /* cannot print dir name */
    575       if (0 != stat (dir_name, &sbuf))
    576         continue;  /* empty */
    577 
    578       /* we ensured always +1k room, filenames are ~256 bytes,
    579          so there is always still enough space for the header
    580          without need for an additional reallocation check. */
    581       res = snprintf (rdc.buf + rdc.off, rdc.buf_size - rdc.off,
    582                       "<h3>%s</h3>\n",
    583                       category);
    584       if (0 >= res)
    585         continue;  /* snprintf() error */
    586       if (rdc.buf_size - rdc.off <= (size_t) res)
    587         continue;  /* buffer too small?? */
    588       rdc.off += (size_t) res;
    589 
    590       if (! list_directory (&rdc,
    591                             dir_name))
    592       {
    593         free (rdc.buf);
    594         update_cached_response (NULL);
    595         return;
    596       }
    597     }
    598   }
    599   /* we ensured always +1k room, filenames are ~256 bytes,
    600      so there is always still enough space for the footer
    601      without need for a final reallocation check. */
    602   len = strlen (INDEX_PAGE_FOOTER);
    603   if (rdc.buf_size - rdc.off < len)
    604   { /* buffer too small */
    605     free (rdc.buf);
    606     update_cached_response (NULL);
    607     return;
    608   }
    609   memcpy (rdc.buf + rdc.off, INDEX_PAGE_FOOTER, len);
    610   rdc.off += len;
    611   initial_allocation = rdc.buf_size; /* remember for next time */
    612   response =
    613     MHD_response_from_buffer (MHD_HTTP_STATUS_OK,
    614                               rdc.off,
    615                               rdc.buf,
    616                               &free,
    617                               rdc.buf);
    618   mark_as_html (response);
    619 #ifdef FORCE_CLOSE
    620   (void) MHD_response_set_option (response,
    621                                   &MHD_R_OPTION_CONN_CLOSE (MHD_YES));
    622 #endif
    623   update_cached_response (response);
    624 }
    625 
    626 
    627 /**
    628  * Context we keep for an upload.
    629  */
    630 struct UploadContext
    631 {
    632   /**
    633    * Handle where we write the uploaded file to.
    634    */
    635   int fd;
    636 
    637   /**
    638    * Name of our temporary file where we initially read to.
    639    */
    640   char tmpname[PATH_MAX + 1];
    641 
    642   /**
    643    * Name of the file on disk.
    644    */
    645   char *filename;
    646 
    647   /**
    648    * True once @a tmpfile exists.
    649    */
    650   bool have_file;
    651 
    652   /**
    653    * True if we had an error handling the upload.
    654    */
    655   bool error_file;
    656 
    657 };
    658 
    659 
    660 /**
    661  * "Stream" reader for POST data.
    662  * This callback is called to incrementally process parsed POST data sent by
    663  * the client.
    664  * The pointers to the MHD_String and MHD_StringNullable are valid only until
    665  * return from this callback.
    666  * The pointers to the strings and the @a data are valid only until return from
    667  * this callback.
    668  *
    669  * @param req the request
    670  * @param cls user-specified closure
    671  * @param name the name of the POST field
    672  * @param filename the name of the uploaded file, @a cstr member is NULL if not
    673  *                 known / not provided
    674  * @param content_type the mime-type of the data, cstr member is NULL if not
    675  *                     known / not provided
    676  * @param encoding the encoding of the data, cstr member is NULL if not known /
    677  *                 not provided
    678  * @param size the number of bytes in @a data available, may be zero if
    679  *             the @a final_data is #MHD_YES
    680  * @param data the pointer to @a size bytes of data at the specified
    681  *             @a off offset, NOT zero-terminated
    682  * @param off the offset of @a data in the overall value, always equal to
    683  *            the sum of sizes of previous calls for the same field / file;
    684  *            client may provide more than one field with the same name and
    685  *            the same filename, the new filed (or file) is indicated by zero
    686  *            value of @a off (and the end is indicated by @a final_data)
    687  * @param final_data if set to #MHD_YES then full field data is provided,
    688  *                   if set to #MHD_NO then more field data may be provided
    689  * @return action specifying how to proceed:
    690  *         #MHD_upload_action_continue() if all is well,
    691  *         #MHD_upload_action_suspend() to stop reading the upload until
    692  *         the request is resumed,
    693  *         #MHD_upload_action_abort_request() to close the socket,
    694  *         or a response to discard the rest of the upload and transmit
    695  *         the response
    696  * @ingroup action
    697  */
    698 static const struct MHD_UploadAction *
    699 stream_reader (struct MHD_Request *req,
    700                void *cls,
    701                const struct MHD_String *name,
    702                const struct MHD_StringNullable *filename,
    703                const struct MHD_StringNullable *content_type,
    704                const struct MHD_StringNullable *encoding,
    705                size_t size,
    706                const void *data,
    707                uint_fast64_t off,
    708                enum MHD_Bool final_data)
    709 {
    710   struct UploadContext *uc = (struct UploadContext *) cls;
    711 
    712   (void) content_type;      /* Unused. Silent compiler warning. */
    713   (void) encoding;          /* Unused. Silent compiler warning. */
    714   (void) off;               /* Unused. Silent compiler warning. */
    715   if ( (0 == strcmp (name->cstr,
    716                      "category")) ||
    717        (0 == strcmp (name->cstr,
    718                      "filename")) ||
    719        (0 == strcmp (name->cstr,
    720                      "language")) )
    721   {
    722     return MHD_upload_action_from_response (req,
    723                                             request_refused_response);
    724   }
    725   if (0 != strcmp (name->cstr,
    726                    "upload"))
    727   {
    728     fprintf (stderr,
    729              "Ignoring unexpected form value `%s'\n",
    730              name->cstr);
    731     return MHD_upload_action_continue (req);
    732   }
    733   if (NULL == filename->cstr)
    734   {
    735     fprintf (stderr,
    736              "No filename, aborting upload.\n");
    737     return MHD_upload_action_from_response (req,
    738                                             request_refused_response);
    739   }
    740   if (-1 == uc->fd)
    741   {
    742     if (0 != filename->len)
    743     {
    744       if ( (NULL != strstr (filename->cstr,
    745                             "..")) ||
    746            (NULL != strchr (filename->cstr,
    747                             '/')) ||
    748            (NULL != strchr (filename->cstr,
    749                             '\\')) )
    750       {
    751         return MHD_upload_action_from_response (req,
    752                                                 request_refused_response);
    753       }
    754       uc->filename = my_strdup (filename->cstr);
    755     }
    756     if (NULL == uc->filename)
    757     {
    758       fprintf (stderr,
    759                "No filename for incremental upload\n");
    760       return MHD_upload_action_from_response (req,
    761                                               internal_error_response);
    762     }
    763 
    764     {
    765       size_t slen = strlen (uc->filename);
    766       size_t i;
    767 
    768       for (i = 0; i < slen; i++)
    769         if (! isprint ((unsigned char) uc->filename[i]))
    770           uc->filename[i] = '_';
    771     }
    772     uc->fd = my_mkstemp (uc->tmpname);
    773     if (-1 == uc->fd)
    774     {
    775       fprintf (stderr,
    776                "Error creating temporary file `%s' for upload: %s\n",
    777                uc->tmpname,
    778                strerror (errno));
    779       return MHD_upload_action_from_response (req,
    780                                               request_refused_response);
    781     }
    782   }
    783   if ( (0 != size) &&
    784 #if ! defined(_WIN32) || defined(__CYGWIN__)
    785        (size !=
    786         (size_t) write (uc->fd,
    787                         data,
    788                         size))
    789 #else  /* Native W32 */
    790        (size !=
    791         (size_t) write (uc->fd,
    792                         data,
    793                         (unsigned int) size))
    794 #endif /* Native W32 */
    795        )
    796   {
    797     /* write failed; likely: disk full */
    798     fprintf (stderr,
    799              "Error writing to file `%s': %s\n",
    800              uc->tmpname,
    801              strerror (errno));
    802     (void) close (uc->fd);
    803     uc->fd = -1;
    804     unlink (uc->tmpname);
    805     return MHD_upload_action_from_response (req,
    806                                             internal_error_response);
    807   }
    808   if (final_data)
    809   {
    810     (void) close (uc->fd);
    811     uc->fd = -1;
    812     uc->have_file = true;
    813   }
    814   return MHD_upload_action_continue (req);
    815 }
    816 
    817 
    818 /**
    819  * Iterator over POST data.
    820  *
    821  * The @a data pointer is valid only until return from this function.
    822  *
    823  * The pointers to the strings in @a data are valid until any MHD_UploadAction
    824  * is provided. If the data is needed beyond this point, it should be copied.
    825  *
    826  * @param cls closure
    827  * @param data the element of the post data, the pointer is valid only until
    828  *             return from this function
    829  * @return #MHD_YES to continue iterating,
    830  *         #MHD_NO to abort the iteration
    831  * @ingroup request
    832  */
    833 static enum MHD_Bool
    834 handle_full_upload (void *cls,
    835                     const struct MHD_PostField *data)
    836 {
    837   struct UploadContext *uc = (struct UploadContext *) cls;
    838   int fd;
    839 
    840   if (0 != strcmp ("upload",
    841                    data->name.cstr))
    842     return MHD_YES;
    843   if (uc->have_file)
    844   {
    845     uc->error_file = true;
    846     return MHD_NO;
    847   }
    848   if (NULL == data->filename.cstr)
    849   {
    850     fprintf (stderr,
    851              "Filename missing for full upload\n");
    852     uc->error_file = true;
    853     return MHD_NO;
    854   }
    855   uc->filename = my_strdup (data->filename.cstr);
    856   fd = my_mkstemp (uc->tmpname);
    857   if (-1 == fd)
    858   {
    859     fprintf (stderr,
    860              "Error creating temporary file `%s' for upload: %s\n",
    861              uc->tmpname,
    862              strerror (errno));
    863     uc->error_file = true;
    864     return MHD_NO;
    865   }
    866   if (NULL != data->value.cstr)
    867   {
    868     // FIXME: error handling, ...
    869 #if ! defined(_WIN32) || defined(__CYGWIN__)
    870     write (fd,
    871            data->value.cstr,
    872            data->value.len);
    873 #else  /* Native W32 */
    874     write (fd,
    875            data->value.cstr,
    876            (unsigned int) data->value.len);
    877 #endif /* Native W32 */
    878   }
    879   close (fd);
    880   uc->have_file = true;
    881   return MHD_NO;
    882 }
    883 
    884 
    885 /**
    886  * The callback to be called when finished with processing
    887  * of the postprocessor upload data.
    888  * @param req the request
    889  * @param cls the closure
    890  * @param parsing_result the result of POST data parsing
    891  * @return the action to proceed
    892  */
    893 static const struct MHD_UploadAction *
    894 done_cb (struct MHD_Request *req,
    895          void *cls,
    896          enum MHD_PostParseResult parsing_result)
    897 {
    898   struct UploadContext *uc = (struct UploadContext *) cls;
    899   const struct MHD_UploadAction *ret;
    900   struct MHD_StringNullable cat;
    901   struct MHD_StringNullable lang;
    902   char fn[PATH_MAX + 1];
    903   int res;
    904 
    905   if (MHD_POST_PARSE_RES_OK != parsing_result)
    906   {
    907     fprintf (stderr,
    908              "Upload parsing failed with status %d\n",
    909              (int) parsing_result);
    910     ret = MHD_upload_action_from_response (req,
    911                                            request_refused_response);
    912     goto cleanup;
    913   }
    914   if (-1 != uc->fd)
    915   {
    916     fprintf (stderr,
    917              "Upload incomplete (fd still open)\n");
    918     (void) close (uc->fd);
    919     if (NULL != uc->filename)
    920     {
    921       fprintf (stderr,
    922                "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
    923                uc->filename);
    924     }
    925     (void) unlink (uc->tmpname);
    926     ret = MHD_upload_action_from_response (req,
    927                                            internal_error_response);
    928     goto cleanup;
    929   }
    930   if (! MHD_request_get_value (req,
    931                                MHD_VK_POSTDATA,
    932                                "category",
    933                                &cat) ||
    934       ! MHD_request_get_value (req,
    935                                MHD_VK_POSTDATA,
    936                                "language",
    937                                &lang) ||
    938       (NULL == cat.cstr) ||
    939       (NULL == lang.cstr) )
    940   {
    941     fprintf (stderr,
    942              "Required argument missing\n");
    943     if (uc->have_file)
    944       (void) unlink (uc->tmpname);
    945     ret = MHD_upload_action_from_response (req,
    946                                            request_refused_response);
    947     goto cleanup;
    948   }
    949   /* FIXME: ugly that we may have to deal with upload
    950      here as well! */
    951   MHD_request_get_post_data_cb (req,
    952                                 &handle_full_upload,
    953                                 uc);
    954   if (uc->error_file)
    955   {
    956     fprintf (stderr,
    957              "Upload data provided twice!\n");
    958     ret = MHD_upload_action_from_response (req,
    959                                            internal_error_response);
    960     goto cleanup;
    961   }
    962   if (NULL == uc->filename)
    963   {
    964     fprintf (stderr,
    965              "Filename unavailable!?\n");
    966     ret = MHD_upload_action_from_response (req,
    967                                            internal_error_response);
    968     goto cleanup;
    969   }
    970   /* create directories -- if they don't exist already */
    971 #if ! defined(_WIN32) || defined(__CYGWIN__)
    972   (void) mkdir (lang.cstr,
    973                 S_IRWXU);
    974 #else
    975   (void) mkdir (lang.cstr);
    976 #endif
    977   res = snprintf (fn,
    978                   sizeof (fn),
    979                   "%s/%s",
    980                   lang.cstr,
    981                   cat.cstr);
    982   if ( (0 >= res) ||
    983        (sizeof (fn) <= (size_t) res) )
    984   {
    985     fprintf (stderr,
    986              "snprintf() failed at %d\n", __LINE__);
    987     ret = MHD_upload_action_from_response (req,
    988                                            request_refused_response);
    989     goto cleanup;
    990   }
    991 #if ! defined(_WIN32) || defined(__CYGWIN__)
    992   (void) mkdir (fn,
    993                 S_IRWXU);
    994 #else
    995   (void) mkdir (fn);
    996 #endif
    997   /* compute filename */
    998   res = snprintf (fn,
    999                   sizeof (fn),
   1000                   "%s/%s/%s",
   1001                   lang.cstr,
   1002                   cat.cstr,
   1003                   uc->filename);
   1004   if ( (0 >= res) ||
   1005        (sizeof (fn) <= (size_t) res) )
   1006   {
   1007     fprintf (stderr,
   1008              "snprintf() failed at %d\n", __LINE__);
   1009     ret = MHD_upload_action_from_response (req,
   1010                                            request_refused_response);
   1011     goto cleanup;
   1012   }
   1013   if (0 !=
   1014       rename (uc->tmpname,
   1015               fn))
   1016   {
   1017     fprintf (stderr,
   1018              "Failed to rename %s to %s: %s\n",
   1019              uc->tmpname,
   1020              fn,
   1021              strerror (errno));
   1022     ret = MHD_upload_action_from_response (req,
   1023                                            request_refused_response);
   1024     goto cleanup;
   1025   }
   1026 #if ! defined(_WIN32) || defined(__CYGWIN__)
   1027   chmod (uc->filename,
   1028          S_IRUSR | S_IWUSR);
   1029 #endif
   1030 
   1031   update_directory ();
   1032   my_mutex_lock (&mutex);
   1033   if (NULL == cached_directory_response)
   1034     ret = MHD_upload_action_from_response (req,
   1035                                            internal_error_response);
   1036   else
   1037     ret = MHD_upload_action_from_response (req,
   1038                                            cached_directory_response);
   1039   my_mutex_unlock (&mutex);
   1040 cleanup:
   1041   if (NULL != uc->filename)
   1042     free (uc->filename);
   1043   free (uc);
   1044   return ret;
   1045 }
   1046 
   1047 
   1048 /**
   1049  * Main callback from MHD, used to generate the page.
   1050  *
   1051  * @param cls NULL
   1052  * @param request the request object
   1053  * @param path the requested uri (without arguments after "?")
   1054  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
   1055  *        #MHD_HTTP_METHOD_PUT, etc.)
   1056  * @param upload_size the size of the message upload content payload,
   1057  *                    #MHD_SIZE_UNKNOWN for chunked uploads (if the
   1058  *                    final chunk has not been processed yet)
   1059  * @return action how to proceed, NULL
   1060  *         if the request must be aborted due to a serious
   1061  *         error while handling the request (implies closure
   1062  *         of underling data stream, for HTTP/1.1 it means
   1063  *         socket closure). */
   1064 static const struct MHD_Action *
   1065 generate_page (void *cls,
   1066                struct MHD_Request *MHD_RESTRICT request,
   1067                const struct MHD_String *MHD_RESTRICT path,
   1068                enum MHD_HTTP_Method method,
   1069                uint_fast64_t upload_size)
   1070 {
   1071   struct MHD_Response *response;
   1072   const char *url = path->cstr;
   1073 
   1074   (void ) cls;
   1075 
   1076   if ((0 != upload_size) &&
   1077       ( (MHD_HTTP_METHOD_GET == method) ||
   1078         (MHD_HTTP_METHOD_HEAD == method) ))
   1079   {
   1080     /* Wrong request, refuse */
   1081     return MHD_action_from_response (request,
   1082                                      request_refused_response);
   1083   }
   1084 
   1085   if ( ( (MHD_HTTP_METHOD_GET == method) ||
   1086          (MHD_HTTP_METHOD_HEAD == method) ) &&
   1087        (0 != strcmp (url,
   1088                      "/")) )
   1089   {
   1090     /* should be file download */
   1091 #ifdef MHD_HAVE_LIBMAGIC
   1092     char file_data[MAGIC_HEADER_SIZE];
   1093     ssize_t got;
   1094 #endif /* MHD_HAVE_LIBMAGIC */
   1095     const char *mime;
   1096     int fd;
   1097     struct stat buf;
   1098 
   1099     fd = -1;
   1100     buf.st_size = 0; /* Mute compiler warning */
   1101     if ((0 != path->len) &&
   1102         (NULL == strstr (&url[1],
   1103                          "..")) &&
   1104         (0 != url[0]) &&
   1105         (0 != url[1]) &&
   1106 #ifdef _WIN32
   1107         ('\\' != url[1]) &&
   1108         ((3 > path->len) || ':' != url[2]) &&
   1109 #endif
   1110         ('/' != url[1]) )
   1111       fd = open (url + 1,
   1112                  O_RDONLY);
   1113     if (-1 != fd)
   1114     {
   1115       if ((0 != fstat (fd,
   1116                        &buf))
   1117           || ! my_S_ISREG (buf.st_mode))
   1118       {
   1119         (void) close (fd);
   1120         fd = -1;
   1121       }
   1122     }
   1123     if (-1 == fd)
   1124       return MHD_action_from_response (request,
   1125                                        file_not_found_response);
   1126 #ifdef MHD_HAVE_LIBMAGIC
   1127     /* read beginning of the file to determine mime type  */
   1128     got = read (fd,
   1129                 file_data,
   1130                 sizeof (file_data));
   1131     (void) lseek (fd,
   1132                   0,
   1133                   SEEK_SET);
   1134     if (0 < got)
   1135       mime = magic_buffer (magic,
   1136                            file_data,
   1137                            (size_t) got);
   1138     else
   1139 #endif /* MHD_HAVE_LIBMAGIC */
   1140     mime = NULL;
   1141     {
   1142       /* Set mime-type by file-extension in some cases */
   1143       const char *ldot = strrchr (&url[1],
   1144                                   '.');
   1145 
   1146       if (NULL != ldot)
   1147       {
   1148         if ((0 == strcmp (ldot,
   1149                           ".html"))
   1150             || (0 == strcmp (ldot,
   1151                              ".htm"))
   1152             || (0 == strcmp (ldot,
   1153                              ".HTML")))
   1154           mime = "text/html";
   1155         if (0 == strcmp (ldot,
   1156                          ".css"))
   1157           mime = "text/css";
   1158         if (0 == strcmp (ldot,
   1159                          ".css3"))
   1160           mime = "text/css";
   1161         if (0 == strcmp (ldot,
   1162                          ".js"))
   1163           mime = "application/javascript";
   1164       }
   1165     }
   1166 
   1167     response = MHD_response_from_fd (MHD_HTTP_STATUS_OK,
   1168                                      fd,
   1169                                      0LLU /* offset */,
   1170                                      (uint_fast64_t) buf.st_size);
   1171     if (NULL == response)
   1172     {
   1173       /* internal error (i.e. out of memory) */
   1174       (void) close (fd);
   1175       return MHD_action_abort_request (request);
   1176     }
   1177 
   1178     /* add mime type if we had one */
   1179     if (NULL != mime)
   1180       (void) MHD_response_add_header (response,
   1181                                       MHD_HTTP_HEADER_CONTENT_TYPE,
   1182                                       mime);
   1183     return MHD_action_from_response (request,
   1184                                      response);
   1185   }
   1186 
   1187   if ( (MHD_HTTP_METHOD_POST == method) &&
   1188        (0 == strcmp (path->cstr,
   1189                      "/")) )
   1190   {
   1191     struct UploadContext *uc;
   1192 
   1193     uc = (struct UploadContext *) malloc (sizeof (struct UploadContext));
   1194     if (NULL == uc)
   1195       return MHD_action_abort_request (request); /* out of memory, close connection */
   1196     memset (uc,
   1197             0,
   1198             sizeof (struct UploadContext));
   1199     strcpy (uc->tmpname,
   1200             "mhd-demo-XXXXXX");
   1201     uc->fd = -1;
   1202     return MHD_action_parse_post (request,
   1203                                   64 * 1024 /* buffer size */,
   1204                                   1024 /* max non-stream size */,
   1205                                   MHD_HTTP_POST_ENCODING_OTHER,
   1206                                   &stream_reader,
   1207                                   uc,
   1208                                   &done_cb,
   1209                                   uc);
   1210   }
   1211   if (  ( (MHD_HTTP_METHOD_GET == method) ||
   1212           (MHD_HTTP_METHOD_HEAD == method) ) &&
   1213         (0 == strcmp (url,
   1214                       "/")) )
   1215   {
   1216     const struct MHD_Action *ret;
   1217 
   1218     my_mutex_lock (&mutex);
   1219     if (NULL == cached_directory_response)
   1220       ret = MHD_action_from_response (request,
   1221                                       internal_error_response);
   1222     else
   1223       ret = MHD_action_from_response (request,
   1224                                       cached_directory_response);
   1225     my_mutex_unlock (&mutex);
   1226     return ret;
   1227   }
   1228   /* unexpected request, refuse */
   1229   return MHD_action_from_response (request,
   1230                                    request_refused_response);
   1231 }
   1232 
   1233 
   1234 #if ! defined(_WIN32) || defined(__CYGWIN__)
   1235 /**
   1236  * Function called if we get a SIGPIPE. Does nothing.
   1237  *
   1238  * @param sig will be SIGPIPE (ignored)
   1239  */
   1240 static void
   1241 catcher (int sig)
   1242 {
   1243   (void) sig;  /* Unused. Silent compiler warning. */
   1244   /* do nothing */
   1245 }
   1246 
   1247 
   1248 /**
   1249  * setup handlers to ignore SIGPIPE.
   1250  */
   1251 static void
   1252 ignore_sigpipe (void)
   1253 {
   1254   struct sigaction oldsig;
   1255   struct sigaction sig;
   1256 
   1257   sig.sa_handler = &catcher;
   1258   sigemptyset (&sig.sa_mask);
   1259 #ifdef SA_INTERRUPT
   1260   sig.sa_flags = SA_INTERRUPT;  /* SunOS */
   1261 #else
   1262   sig.sa_flags = SA_RESTART;
   1263 #endif
   1264   if (0 != sigaction (SIGPIPE,
   1265                       &sig,
   1266                       &oldsig))
   1267     fprintf (stderr,
   1268              "Failed to install SIGPIPE handler: %s\n", strerror (errno));
   1269 }
   1270 
   1271 
   1272 #endif
   1273 
   1274 #if defined(_WIN32) && ! defined(__CYGWIN__)
   1275 static void
   1276 set_utf8_locale (void)
   1277 {
   1278   if (NULL != setlocale (LC_CTYPE, ".UTF8"))
   1279     return; /* Success */
   1280   (void) setlocale (LC_CTYPE, "C"); /* A fallback, not really helpful */
   1281 }
   1282 
   1283 
   1284 #endif
   1285 
   1286 /**
   1287  * Entry point to demo.  Note: this HTTP server will make all
   1288  * files in the current directory and its subdirectories available
   1289  * to anyone.  Press ENTER to stop the server once it has started.
   1290  *
   1291  * @param argc number of arguments in argv
   1292  * @param argv first and only argument should be the port number
   1293  * @return 0 on success
   1294  */
   1295 int
   1296 main (int argc,
   1297       char *const *argv)
   1298 {
   1299   struct MHD_Daemon *d;
   1300   unsigned int port;
   1301   char dummy;
   1302 
   1303   if ( (argc != 2) ||
   1304        (1 != sscanf (argv[1],
   1305                      "%u%c",
   1306                      &port,
   1307                      &dummy)) ||
   1308        (UINT16_MAX < port) )
   1309   {
   1310     if (2 == argc)
   1311     {
   1312       fprintf (stderr,
   1313                "Usage: %s PORT\n",
   1314                argv[0]);
   1315       return 1;
   1316     }
   1317     port = 8080;
   1318   }
   1319 #if ! defined(_WIN32) || defined(__CYGWIN__)
   1320   ignore_sigpipe ();
   1321 #endif
   1322 #if defined(_WIN32) && ! defined(__CYGWIN__)
   1323   set_utf8_locale ();
   1324 #endif
   1325 #ifdef MHD_HAVE_LIBMAGIC
   1326   magic = magic_open (MAGIC_MIME_TYPE);
   1327   (void) magic_load (magic, NULL);
   1328 #endif /* MHD_HAVE_LIBMAGIC */
   1329 
   1330   my_mutex_init (&mutex);
   1331   file_not_found_response =
   1332     MHD_response_from_buffer_static (
   1333       MHD_HTTP_STATUS_NOT_FOUND,
   1334       strlen (FILE_NOT_FOUND_PAGE),
   1335       FILE_NOT_FOUND_PAGE);
   1336   mark_as_html (file_not_found_response);
   1337   if (MHD_SC_OK !=
   1338       MHD_response_set_option (file_not_found_response,
   1339                                &MHD_R_OPTION_REUSABLE (MHD_YES)))
   1340     return 1;
   1341   request_refused_response =
   1342     MHD_response_from_buffer_static (
   1343       MHD_HTTP_STATUS_FORBIDDEN,
   1344       strlen (REQUEST_REFUSED_PAGE),
   1345       REQUEST_REFUSED_PAGE);
   1346   mark_as_html (request_refused_response);
   1347   if (MHD_SC_OK !=
   1348       MHD_response_set_option (request_refused_response,
   1349                                &MHD_R_OPTION_REUSABLE (MHD_YES)))
   1350     return 1;
   1351   internal_error_response =
   1352     MHD_response_from_buffer_static (
   1353       MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
   1354       strlen (INTERNAL_ERROR_PAGE),
   1355       INTERNAL_ERROR_PAGE);
   1356   mark_as_html (internal_error_response);
   1357   if (MHD_SC_OK !=
   1358       MHD_response_set_option (internal_error_response,
   1359                                &MHD_R_OPTION_REUSABLE (MHD_YES)))
   1360     return 1;
   1361   update_directory ();
   1362   d = MHD_daemon_create (&generate_page,
   1363                          NULL);
   1364   if (NULL == d)
   1365     return 1;
   1366   if (MHD_SC_OK !=
   1367       MHD_DAEMON_SET_OPTIONS (
   1368         d,
   1369         MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO),
   1370         MHD_D_OPTION_WM_WORKER_THREADS (NUMBER_OF_THREADS),
   1371         MHD_D_OPTION_DEFAULT_TIMEOUT (120 /* seconds */),
   1372         MHD_D_OPTION_CONN_MEMORY_LIMIT (256 * 1024),
   1373         MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
   1374                                 (uint_least16_t) port)))
   1375     return 1;
   1376 #ifdef PRODUCTION
   1377   if (MHD_SC_OK !=
   1378       MHD_DAEMON_SET_OPTIONS (
   1379         d,
   1380         MHD_D_OPTION_PER_IP_LIMIT (64))
   1381       return 1;
   1382 #endif
   1383   if (MHD_SC_OK !=
   1384       MHD_daemon_start (d))
   1385   {
   1386     MHD_daemon_destroy (d);
   1387     return 1;
   1388   }
   1389   fprintf (stderr,
   1390            "HTTP server running on port %u. Press ENTER to stop the server.\n",
   1391            port);
   1392   (void) getc (stdin);
   1393   MHD_daemon_destroy (d);
   1394   MHD_response_destroy (file_not_found_response);
   1395   MHD_response_destroy (request_refused_response);
   1396   MHD_response_destroy (internal_error_response);
   1397   update_cached_response (NULL);
   1398   my_mutex_destroy (&mutex);
   1399 #ifdef MHD_HAVE_LIBMAGIC
   1400   magic_close (magic);
   1401 #endif /* MHD_HAVE_LIBMAGIC */
   1402   return 0;
   1403 }
   1404 
   1405 
   1406 /* end of demo.c */