libmicrohttpd

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

demo_https.c (32969B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2013 Christian Grothoff (and other contributing authors)
      4      Copyright (C) 2016-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 /**
     22  * @file demo_https.c
     23  * @brief complex demonstration site: create directory index, offer
     24  *        upload via form and HTTP POST, download with mime type detection
     25  *        and error reporting (403, etc.) --- and all of this with
     26  *        high-performance settings (large buffers, thread pool).
     27  *        If you want to benchmark MHD, this code should be used to
     28  *        run tests against.  Note that the number of threads may need
     29  *        to be adjusted depending on the number of available cores.
     30  *        Logic is identical to demo.c, just adds HTTPS support.
     31  *        This demonstration uses key/cert stored in static string. Optionally,
     32  *        use gnutls_load_file() to load them from file.
     33  * @author Christian Grothoff
     34  * @author Karlson2k (Evgeny Grin)
     35  */
     36 #include "platform.h"
     37 #include <microhttpd.h>
     38 #include <unistd.h>
     39 #include <pthread.h>
     40 #include <sys/types.h>
     41 #include <sys/stat.h>
     42 #include <dirent.h>
     43 #ifdef MHD_HAVE_LIBMAGIC
     44 #include <magic.h>
     45 #endif /* MHD_HAVE_LIBMAGIC */
     46 #include <limits.h>
     47 #include <ctype.h>
     48 #include <errno.h>
     49 
     50 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     51 #undef MHD_CPU_COUNT
     52 #endif
     53 #if ! defined(MHD_CPU_COUNT)
     54 #define MHD_CPU_COUNT 2
     55 #endif
     56 
     57 #ifndef PATH_MAX
     58 /* Some platforms (namely: GNU Hurd) do no define PATH_MAX.
     59    As it is only example for MHD, just use reasonable value for PATH_MAX. */
     60 #define PATH_MAX 16384
     61 #endif
     62 
     63 /**
     64  * Number of threads to run in the thread pool.  Should (roughly) match
     65  * the number of cores on your system.
     66  */
     67 #define NUMBER_OF_THREADS MHD_CPU_COUNT
     68 
     69 #ifdef MHD_HAVE_LIBMAGIC
     70 /**
     71  * How many bytes of a file do we give to libmagic to determine the mime type?
     72  * 16k might be a bit excessive, but ought not hurt performance much anyway,
     73  * and should definitively be on the safe side.
     74  */
     75 #define MAGIC_HEADER_SIZE (16 * 1024)
     76 #endif /* MHD_HAVE_LIBMAGIC */
     77 
     78 
     79 /**
     80  * Page returned for file-not-found.
     81  */
     82 #define FILE_NOT_FOUND_PAGE \
     83   "<html><head><title>File not found</title></head><body>File not found</body></html>"
     84 
     85 
     86 /**
     87  * Page returned for internal errors.
     88  */
     89 #define INTERNAL_ERROR_PAGE \
     90   "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
     91 
     92 
     93 /**
     94  * Page returned for refused requests.
     95  */
     96 #define REQUEST_REFUSED_PAGE \
     97   "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
     98 
     99 
    100 /**
    101  * Head of index page.
    102  */
    103 #define INDEX_PAGE_HEADER \
    104   "<html>\n<head><title>Welcome</title></head>\n<body>\n" \
    105   "<h1>Upload</h1>\n" \
    106   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n" \
    107   "<dl><dt>Content type:</dt><dd>" \
    108   "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>" \
    109   "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>" \
    110   "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>" \
    111   "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>" \
    112   "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n" \
    113   "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>" \
    114   "<dt>Language:</dt><dd>" \
    115   "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>" \
    116   "<input type=\"radio\" name=\"language\" value=\"en\">English</input>" \
    117   "<input type=\"radio\" name=\"language\" value=\"de\">German</input>" \
    118   "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>" \
    119   "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n" \
    120   "<dt>File:</dt><dd>" \
    121   "<input type=\"file\" name=\"upload\"/></dd></dl>" \
    122   "<input type=\"submit\" value=\"Send!\"/>\n" \
    123   "</form>\n" \
    124   "<h1>Download</h1>\n" \
    125   "<ol>\n"
    126 
    127 /**
    128  * Footer of index page.
    129  */
    130 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
    131 
    132 
    133 /**
    134  * NULL-terminated array of supported upload categories.  Should match HTML
    135  * in the form.
    136  */
    137 static const char *const categories[] = {
    138   "books",
    139   "images",
    140   "music",
    141   "software",
    142   "videos",
    143   "other",
    144   NULL,
    145 };
    146 
    147 
    148 /**
    149  * Specification of a supported language.
    150  */
    151 struct Language
    152 {
    153   /**
    154    * Directory name for the language.
    155    */
    156   const char *dirname;
    157 
    158   /**
    159    * Long name for humans.
    160    */
    161   const char *longname;
    162 
    163 };
    164 
    165 /**
    166  * NULL-terminated array of supported upload categories.  Should match HTML
    167  * in the form.
    168  */
    169 static const struct Language languages[] = {
    170   { "no-lang", "No language specified" },
    171   { "en", "English" },
    172   { "de", "German" },
    173   { "fr", "French" },
    174   { "es", "Spanish" },
    175   { NULL, NULL },
    176 };
    177 
    178 
    179 /**
    180  * Response returned if the requested file does not exist (or is not accessible).
    181  */
    182 static struct MHD_Response *file_not_found_response;
    183 
    184 /**
    185  * Response returned for internal errors.
    186  */
    187 static struct MHD_Response *internal_error_response;
    188 
    189 /**
    190  * Response returned for '/' (GET) to list the contents of the directory and allow upload.
    191  */
    192 static struct MHD_Response *cached_directory_response;
    193 
    194 /**
    195  * Response returned for refused uploads.
    196  */
    197 static struct MHD_Response *request_refused_response;
    198 
    199 /**
    200  * Mutex used when we update the cached directory response object.
    201  */
    202 static pthread_mutex_t mutex;
    203 
    204 #ifdef MHD_HAVE_LIBMAGIC
    205 /**
    206  * Global handle to MAGIC data.
    207  */
    208 static magic_t magic;
    209 #endif /* MHD_HAVE_LIBMAGIC */
    210 
    211 
    212 /**
    213  * Mark the given response as HTML for the browser.
    214  *
    215  * @param response response to mark
    216  */
    217 static void
    218 mark_as_html (struct MHD_Response *response)
    219 {
    220   (void) MHD_add_response_header (response,
    221                                   MHD_HTTP_HEADER_CONTENT_TYPE,
    222                                   "text/html");
    223 }
    224 
    225 
    226 /**
    227  * Replace the existing 'cached_directory_response' with the
    228  * given response.
    229  *
    230  * @param response new directory response
    231  */
    232 static void
    233 update_cached_response (struct MHD_Response *response)
    234 {
    235   (void) pthread_mutex_lock (&mutex);
    236   if (NULL != cached_directory_response)
    237     MHD_destroy_response (cached_directory_response);
    238   cached_directory_response = response;
    239   (void) pthread_mutex_unlock (&mutex);
    240 }
    241 
    242 
    243 /**
    244  * Context keeping the data for the response we're building.
    245  */
    246 struct ResponseDataContext
    247 {
    248   /**
    249    * Response data string.
    250    */
    251   char *buf;
    252 
    253   /**
    254    * Number of bytes allocated for 'buf'.
    255    */
    256   size_t buf_len;
    257 
    258   /**
    259    * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
    260    */
    261   size_t off;
    262 
    263 };
    264 
    265 
    266 /**
    267  * Create a listing of the files in 'dirname' in HTML.
    268  *
    269  * @param rdc where to store the list of files
    270  * @param dirname name of the directory to list
    271  * @return MHD_YES on success, MHD_NO on error
    272  */
    273 static enum MHD_Result
    274 list_directory (struct ResponseDataContext *rdc,
    275                 const char *dirname)
    276 {
    277   char fullname[PATH_MAX];
    278   struct stat sbuf;
    279   DIR *dir;
    280   struct dirent *de;
    281 
    282   if (NULL == (dir = opendir (dirname)))
    283     return MHD_NO;
    284   while (NULL != (de = readdir (dir)))
    285   {
    286     int res;
    287     if ('.' == de->d_name[0])
    288       continue;
    289     if (sizeof (fullname) <= (size_t)
    290         snprintf (fullname, sizeof (fullname),
    291                   "%s/%s",
    292                   dirname, de->d_name))
    293       continue;  /* ugh, file too long? how can this be!? */
    294     if (0 != stat (fullname, &sbuf))
    295       continue;  /* ugh, failed to 'stat' */
    296     if (! S_ISREG (sbuf.st_mode))
    297       continue;  /* not a regular file, skip */
    298     if (rdc->off + 1024 > rdc->buf_len)
    299     {
    300       void *r;
    301 
    302       if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
    303         break; /* more than SIZE_T _index_ size? Too big for us */
    304       rdc->buf_len = 2 * rdc->buf_len + 1024;
    305       if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
    306         break; /* out of memory */
    307       rdc->buf = r;
    308     }
    309     res = snprintf (&rdc->buf[rdc->off],
    310                     rdc->buf_len - rdc->off,
    311                     "<li><a href=\"/%s\">%s</a></li>\n",
    312                     fullname,
    313                     de->d_name);
    314     if (0 >= res)
    315       continue;  /* snprintf() error */
    316     if (rdc->buf_len - rdc->off <= (size_t) res)
    317       continue;  /* buffer too small?? */
    318     rdc->off += (size_t) res;
    319   }
    320   (void) closedir (dir);
    321   return MHD_YES;
    322 }
    323 
    324 
    325 /**
    326  * Re-scan our local directory and re-build the index.
    327  */
    328 static void
    329 update_directory (void)
    330 {
    331   static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
    332   struct MHD_Response *response;
    333   struct ResponseDataContext rdc;
    334   unsigned int language_idx;
    335   unsigned int category_idx;
    336   const struct Language *language;
    337   const char *category;
    338   char dir_name[128];
    339   struct stat sbuf;
    340   int res;
    341   size_t len;
    342 
    343   rdc.buf_len = initial_allocation;
    344   if (NULL == (rdc.buf = malloc (rdc.buf_len)))
    345   {
    346     update_cached_response (NULL);
    347     return;
    348   }
    349   len = strlen (INDEX_PAGE_HEADER);
    350   if (rdc.buf_len <= len)
    351   { /* buffer too small */
    352     free (rdc.buf);
    353     update_cached_response (NULL);
    354     return;
    355   }
    356   memcpy (rdc.buf, INDEX_PAGE_HEADER, len);
    357   rdc.off = len;
    358   for (language_idx = 0; NULL != languages[language_idx].dirname;
    359        language_idx++)
    360   {
    361     language = &languages[language_idx];
    362 
    363     if (0 != stat (language->dirname, &sbuf))
    364       continue; /* empty */
    365     /* we ensured always +1k room, filenames are ~256 bytes,
    366        so there is always still enough space for the header
    367        without need for an additional reallocation check. */
    368     res = snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    369                     "<h2>%s</h2>\n",
    370                     language->longname);
    371     if (0 >= res)
    372       continue;  /* snprintf() error */
    373     if (rdc.buf_len - rdc.off <= (size_t) res)
    374       continue;  /* buffer too small?? */
    375     rdc.off += (size_t) res;
    376     for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
    377     {
    378       category = categories[category_idx];
    379       res = snprintf (dir_name, sizeof (dir_name),
    380                       "%s/%s",
    381                       language->dirname,
    382                       category);
    383       if ((0 >= res) || (sizeof (dir_name) <= (size_t) res))
    384         continue;  /* cannot print dir name */
    385       if (0 != stat (dir_name, &sbuf))
    386         continue;  /* empty */
    387 
    388       /* we ensured always +1k room, filenames are ~256 bytes,
    389          so there is always still enough space for the header
    390          without need for an additional reallocation check. */
    391       res = snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    392                       "<h3>%s</h3>\n",
    393                       category);
    394       if (0 >= res)
    395         continue;  /* snprintf() error */
    396       if (rdc.buf_len - rdc.off <= (size_t) res)
    397         continue;  /* buffer too small?? */
    398       rdc.off += (size_t) res;
    399 
    400       if (MHD_NO == list_directory (&rdc, dir_name))
    401       {
    402         free (rdc.buf);
    403         update_cached_response (NULL);
    404         return;
    405       }
    406     }
    407   }
    408   /* we ensured always +1k room, filenames are ~256 bytes,
    409      so there is always still enough space for the footer
    410      without need for a final reallocation check. */
    411   len = strlen (INDEX_PAGE_FOOTER);
    412   if (rdc.buf_len - rdc.off <= len)
    413   { /* buffer too small */
    414     free (rdc.buf);
    415     update_cached_response (NULL);
    416     return;
    417   }
    418   memcpy (&rdc.buf[rdc.off], INDEX_PAGE_FOOTER, len);
    419   rdc.off += len;
    420   initial_allocation = rdc.buf_len; /* remember for next time */
    421   response =
    422     MHD_create_response_from_buffer_with_free_callback (rdc.off,
    423                                                         rdc.buf,
    424                                                         &free);
    425   mark_as_html (response);
    426 #ifdef FORCE_CLOSE
    427   (void) MHD_add_response_header (response,
    428                                   MHD_HTTP_HEADER_CONNECTION,
    429                                   "close");
    430 #endif
    431   update_cached_response (response);
    432 }
    433 
    434 
    435 /**
    436  * Context we keep for an upload.
    437  */
    438 struct UploadContext
    439 {
    440   /**
    441    * Handle where we write the uploaded file to.
    442    */
    443   int fd;
    444 
    445   /**
    446    * Name of the file on disk (used to remove on errors).
    447    */
    448   char *filename;
    449 
    450   /**
    451    * Language for the upload.
    452    */
    453   char *language;
    454 
    455   /**
    456    * Category for the upload.
    457    */
    458   char *category;
    459 
    460   /**
    461    * Post processor we're using to process the upload.
    462    */
    463   struct MHD_PostProcessor *pp;
    464 
    465   /**
    466    * Handle to connection that we're processing the upload for.
    467    */
    468   struct MHD_Connection *connection;
    469 
    470   /**
    471    * Response to generate, NULL to use directory.
    472    */
    473   struct MHD_Response *response;
    474 };
    475 
    476 
    477 /**
    478  * Append the 'size' bytes from 'data' to '*ret', adding
    479  * 0-termination.  If '*ret' is NULL, allocate an empty string first.
    480  *
    481  * @param ret string to update, NULL or 0-terminated
    482  * @param data data to append
    483  * @param size number of bytes in 'data'
    484  * @return #MHD_NO on allocation failure, #MHD_YES on success
    485  */
    486 static enum MHD_Result
    487 do_append (char **ret,
    488            const char *data,
    489            size_t size)
    490 {
    491   char *buf;
    492   size_t old_len;
    493 
    494   if (NULL == *ret)
    495     old_len = 0;
    496   else
    497     old_len = strlen (*ret);
    498   if (NULL == (buf = malloc (old_len + size + 1)))
    499     return MHD_NO;
    500   if (NULL != *ret)
    501   {
    502     memcpy (buf,
    503             *ret,
    504             old_len);
    505     free (*ret);
    506   }
    507   memcpy (&buf[old_len],
    508           data,
    509           size);
    510   buf[old_len + size] = '\0';
    511   *ret = buf;
    512   return MHD_YES;
    513 }
    514 
    515 
    516 /**
    517  * Iterator over key-value pairs where the value
    518  * maybe made available in increments and/or may
    519  * not be zero-terminated.  Used for processing
    520  * POST data.
    521  *
    522  * @param cls user-specified closure
    523  * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
    524  * @param key 0-terminated key for the value
    525  * @param filename name of the uploaded file, NULL if not known
    526  * @param content_type mime-type of the data, NULL if not known
    527  * @param transfer_encoding encoding of the data, NULL if not known
    528  * @param data pointer to size bytes of data at the
    529  *              specified offset
    530  * @param off offset of data in the overall value
    531  * @param size number of bytes in data available
    532  * @return #MHD_YES to continue iterating,
    533  *         #MHD_NO to abort the iteration
    534  */
    535 static enum MHD_Result
    536 process_upload_data (void *cls,
    537                      enum MHD_ValueKind kind,
    538                      const char *key,
    539                      const char *filename,
    540                      const char *content_type,
    541                      const char *transfer_encoding,
    542                      const char *data,
    543                      uint64_t off,
    544                      size_t size)
    545 {
    546   struct UploadContext *uc = cls;
    547   size_t i;
    548   int res;
    549   (void) kind;              /* Unused. Silent compiler warning. */
    550   (void) content_type;      /* Unused. Silent compiler warning. */
    551   (void) transfer_encoding; /* Unused. Silent compiler warning. */
    552   (void) off;               /* Unused. Silent compiler warning. */
    553 
    554   if (0 == strcmp (key, "category"))
    555     return do_append (&uc->category, data, size);
    556   if (0 == strcmp (key, "language"))
    557     return do_append (&uc->language, data, size);
    558   if (0 != strcmp (key, "upload"))
    559   {
    560     fprintf (stderr,
    561              "Ignoring unexpected form value `%s'\n",
    562              key);
    563     return MHD_YES;   /* ignore */
    564   }
    565   if (NULL == filename)
    566   {
    567     fprintf (stderr, "No filename, aborting upload.\n");
    568     return MHD_NO;   /* no filename, error */
    569   }
    570   if ( (NULL == uc->category) ||
    571        (NULL == uc->language) )
    572   {
    573     fprintf (stderr,
    574              "Missing form data for upload `%s'\n",
    575              filename);
    576     uc->response = request_refused_response;
    577     return MHD_NO;
    578   }
    579   if (-1 == uc->fd)
    580   {
    581     char fn[PATH_MAX];
    582 
    583     if ( (NULL != strstr (filename, "..")) ||
    584          (NULL != strchr (filename, '/')) ||
    585          (NULL != strchr (filename, '\\')) )
    586     {
    587       uc->response = request_refused_response;
    588       return MHD_NO;
    589     }
    590     /* create directories -- if they don't exist already */
    591 #ifdef WINDOWS
    592     (void) mkdir (uc->language);
    593 #else
    594     (void) mkdir (uc->language, S_IRWXU);
    595 #endif
    596     snprintf (fn, sizeof (fn),
    597               "%s/%s",
    598               uc->language,
    599               uc->category);
    600 #ifdef WINDOWS
    601     (void) mkdir (fn);
    602 #else
    603     (void) mkdir (fn, S_IRWXU);
    604 #endif
    605     /* open file */
    606     res = snprintf (fn, sizeof (fn),
    607                     "%s/%s/%s",
    608                     uc->language,
    609                     uc->category,
    610                     filename);
    611     if ((0 >= res) || (sizeof (fn) <= (size_t) res))
    612     {
    613       uc->response = request_refused_response;
    614       return MHD_NO;
    615     }
    616     for (i = 0; i < (size_t) res; i++)
    617       if (! isprint ((unsigned char) fn[i]))
    618         fn[i] = '_';
    619     uc->fd = open (fn,
    620                    O_CREAT | O_EXCL
    621 #ifdef O_LARGEFILE
    622                    | O_LARGEFILE
    623 #endif
    624                    | O_WRONLY,
    625                    S_IRUSR | S_IWUSR);
    626     if (-1 == uc->fd)
    627     {
    628       fprintf (stderr,
    629                "Error opening file `%s' for upload: %s\n",
    630                fn,
    631                strerror (errno));
    632       uc->response = request_refused_response;
    633       return MHD_NO;
    634     }
    635     uc->filename = strdup (fn);
    636   }
    637   if ( (0 != size) &&
    638 #if ! defined(_WIN32) || defined(__CYGWIN__)
    639        (size != (size_t) write (uc->fd, data, size))
    640 #else  /* Native W32 */
    641        (size != (size_t) write (uc->fd, data, (unsigned int) size))
    642 #endif /* Native W32 */
    643        )
    644   {
    645     /* write failed; likely: disk full */
    646     fprintf (stderr,
    647              "Error writing to file `%s': %s\n",
    648              uc->filename,
    649              strerror (errno));
    650     uc->response = internal_error_response;
    651     (void) close (uc->fd);
    652     uc->fd = -1;
    653     if (NULL != uc->filename)
    654     {
    655       unlink (uc->filename);
    656       free (uc->filename);
    657       uc->filename = NULL;
    658     }
    659     return MHD_NO;
    660   }
    661   return MHD_YES;
    662 }
    663 
    664 
    665 /**
    666  * Function called whenever a request was completed.
    667  * Used to clean up 'struct UploadContext' objects.
    668  *
    669  * @param cls client-defined closure, NULL
    670  * @param connection connection handle
    671  * @param req_cls value as set by the last call to
    672  *        the MHD_AccessHandlerCallback, points to NULL if this was
    673  *            not an upload
    674  * @param toe reason for request termination
    675  */
    676 static void
    677 response_completed_callback (void *cls,
    678                              struct MHD_Connection *connection,
    679                              void **req_cls,
    680                              enum MHD_RequestTerminationCode toe)
    681 {
    682   struct UploadContext *uc = *req_cls;
    683   (void) cls;         /* Unused. Silent compiler warning. */
    684   (void) connection;  /* Unused. Silent compiler warning. */
    685   (void) toe;         /* Unused. Silent compiler warning. */
    686 
    687   if (NULL == uc)
    688     return; /* this request wasn't an upload request */
    689   if (NULL != uc->pp)
    690   {
    691     MHD_destroy_post_processor (uc->pp);
    692     uc->pp = NULL;
    693   }
    694   if (-1 != uc->fd)
    695   {
    696     (void) close (uc->fd);
    697     if (NULL != uc->filename)
    698     {
    699       fprintf (stderr,
    700                "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
    701                uc->filename);
    702       (void) unlink (uc->filename);
    703     }
    704   }
    705   if (NULL != uc->filename)
    706     free (uc->filename);
    707   free (uc);
    708 }
    709 
    710 
    711 /**
    712  * Return the current directory listing.
    713  *
    714  * @param connection connection to return the directory for
    715  * @return MHD_YES on success, MHD_NO on error
    716  */
    717 static enum MHD_Result
    718 return_directory_response (struct MHD_Connection *connection)
    719 {
    720   enum MHD_Result ret;
    721 
    722   (void) pthread_mutex_lock (&mutex);
    723   if (NULL == cached_directory_response)
    724     ret = MHD_queue_response (connection,
    725                               MHD_HTTP_INTERNAL_SERVER_ERROR,
    726                               internal_error_response);
    727   else
    728     ret = MHD_queue_response (connection,
    729                               MHD_HTTP_OK,
    730                               cached_directory_response);
    731   (void) pthread_mutex_unlock (&mutex);
    732   return ret;
    733 }
    734 
    735 
    736 /**
    737  * Main callback from MHD, used to generate the page.
    738  *
    739  * @param cls NULL
    740  * @param connection connection handle
    741  * @param url requested URL
    742  * @param method GET, PUT, POST, etc.
    743  * @param version HTTP version
    744  * @param upload_data data from upload (PUT/POST)
    745  * @param upload_data_size number of bytes in "upload_data"
    746  * @param req_cls our context
    747  * @return #MHD_YES on success, #MHD_NO to drop connection
    748  */
    749 static enum MHD_Result
    750 generate_page (void *cls,
    751                struct MHD_Connection *connection,
    752                const char *url,
    753                const char *method,
    754                const char *version,
    755                const char *upload_data,
    756                size_t *upload_data_size, void **req_cls)
    757 {
    758   struct MHD_Response *response;
    759   enum MHD_Result ret;
    760   int fd;
    761   struct stat buf;
    762   (void) cls;               /* Unused. Silent compiler warning. */
    763   (void) version;           /* Unused. Silent compiler warning. */
    764 
    765   if (0 != strcmp (url, "/"))
    766   {
    767     /* should be file download */
    768 #ifdef MHD_HAVE_LIBMAGIC
    769     char file_data[MAGIC_HEADER_SIZE];
    770     ssize_t got;
    771 #endif /* MHD_HAVE_LIBMAGIC */
    772     const char *mime;
    773 
    774     if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    775       return MHD_NO; /* unexpected method (we're not polite...) */
    776     fd = -1;
    777     if ( (NULL == strstr (&url[1], "..")) &&
    778          ('/' != url[1]) )
    779     {
    780       fd = open (&url[1], O_RDONLY);
    781       if ( (-1 != fd) &&
    782            ( (0 != fstat (fd, &buf)) ||
    783              (! S_ISREG (buf.st_mode)) ) )
    784       {
    785         (void) close (fd);
    786         fd = -1;
    787       }
    788     }
    789     if (-1 == fd)
    790       return MHD_queue_response (connection,
    791                                  MHD_HTTP_NOT_FOUND,
    792                                  file_not_found_response);
    793 #ifdef MHD_HAVE_LIBMAGIC
    794     /* read beginning of the file to determine mime type  */
    795     got = read (fd, file_data, sizeof (file_data));
    796     (void) lseek (fd, 0, SEEK_SET);
    797     if (0 < got)
    798       mime = magic_buffer (magic, file_data, (size_t) got);
    799     else
    800 #endif /* MHD_HAVE_LIBMAGIC */
    801     mime = NULL;
    802 
    803     if (NULL == (response = MHD_create_response_from_fd ((size_t) buf.st_size,
    804                                                          fd)))
    805     {
    806       /* internal error (i.e. out of memory) */
    807       (void) close (fd);
    808       return MHD_NO;
    809     }
    810 
    811     /* add mime type if we had one */
    812     if (NULL != mime)
    813       (void) MHD_add_response_header (response,
    814                                       MHD_HTTP_HEADER_CONTENT_TYPE,
    815                                       mime);
    816     ret = MHD_queue_response (connection,
    817                               MHD_HTTP_OK,
    818                               response);
    819     MHD_destroy_response (response);
    820     return ret;
    821   }
    822 
    823   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    824   {
    825     /* upload! */
    826     struct UploadContext *uc = *req_cls;
    827 
    828     if (NULL == uc)
    829     {
    830       if (NULL == (uc = malloc (sizeof (struct UploadContext))))
    831         return MHD_NO; /* out of memory, close connection */
    832       memset (uc, 0, sizeof (struct UploadContext));
    833       uc->fd = -1;
    834       uc->connection = connection;
    835       uc->pp = MHD_create_post_processor (connection,
    836                                           64 * 1024 /* buffer size */,
    837                                           &process_upload_data, uc);
    838       if (NULL == uc->pp)
    839       {
    840         /* out of memory, close connection */
    841         free (uc);
    842         return MHD_NO;
    843       }
    844       *req_cls = uc;
    845       return MHD_YES;
    846     }
    847     if (0 != *upload_data_size)
    848     {
    849       if (NULL == uc->response)
    850         (void) MHD_post_process (uc->pp,
    851                                  upload_data,
    852                                  *upload_data_size);
    853       *upload_data_size = 0;
    854       return MHD_YES;
    855     }
    856     /* end of upload, finish it! */
    857     MHD_destroy_post_processor (uc->pp);
    858     uc->pp = NULL;
    859     if (-1 != uc->fd)
    860     {
    861       close (uc->fd);
    862       uc->fd = -1;
    863     }
    864     if (NULL != uc->response)
    865     {
    866       return MHD_queue_response (connection,
    867                                  MHD_HTTP_FORBIDDEN,
    868                                  uc->response);
    869     }
    870     else
    871     {
    872       update_directory ();
    873       return return_directory_response (connection);
    874     }
    875   }
    876   if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
    877   {
    878     return return_directory_response (connection);
    879   }
    880 
    881   /* unexpected request, refuse */
    882   return MHD_queue_response (connection,
    883                              MHD_HTTP_FORBIDDEN,
    884                              request_refused_response);
    885 }
    886 
    887 
    888 #ifndef MINGW
    889 /**
    890  * Function called if we get a SIGPIPE. Does nothing.
    891  *
    892  * @param sig will be SIGPIPE (ignored)
    893  */
    894 static void
    895 catcher (int sig)
    896 {
    897   (void) sig;  /* Unused. Silent compiler warning. */
    898   /* do nothing */
    899 }
    900 
    901 
    902 /**
    903  * setup handlers to ignore SIGPIPE.
    904  */
    905 static void
    906 ignore_sigpipe (void)
    907 {
    908   struct sigaction oldsig;
    909   struct sigaction sig;
    910 
    911   sig.sa_handler = &catcher;
    912   sigemptyset (&sig.sa_mask);
    913 #ifdef SA_INTERRUPT
    914   sig.sa_flags = SA_INTERRUPT;  /* SunOS */
    915 #else
    916   sig.sa_flags = SA_RESTART;
    917 #endif
    918   if (0 != sigaction (SIGPIPE, &sig, &oldsig))
    919     fprintf (stderr,
    920              "Failed to install SIGPIPE handler: %s\n", strerror (errno));
    921 }
    922 
    923 
    924 #endif
    925 
    926 /* test server key */
    927 static const char srv_signed_key_pem[] =
    928   "-----BEGIN PRIVATE KEY-----\n\
    929 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCff7amw9zNSE+h\n\
    930 rOMhBrzbbsJluUP3gmd8nOKY5MUimoPkxmAXfp2L0il+MPZT/ZEmo11q0k6J2jfG\n\
    931 UBQ+oZW9ahNZ9gCDjbYlBblo/mqTai+LdeLO3qk53d0zrZKXvCO6sA3uKpG2WR+g\n\
    932 +sNKxfYpIHCpanqBU6O+degIV/+WKy3nQ2Fwp7K5HUNj1u0pg0QQ18yf68LTnKFU\n\
    933 HFjZmmaaopWki5wKSBieHivzQy6w+04HSTogHHRK/y/UcoJNSG7xnHmoPPo1vLT8\n\
    934 CMRIYnSSgU3wJ43XBJ80WxrC2dcoZjV2XZz+XdQwCD4ZrC1ihykcAmiQA+sauNm7\n\
    935 dztOMkGzAgMBAAECggEAIbKDzlvXDG/YkxnJqrKXt+yAmak4mNQuNP+YSCEdHSBz\n\
    936 +SOILa6MbnvqVETX5grOXdFp7SWdfjZiTj2g6VKOJkSA7iKxHRoVf2DkOTB3J8np\n\
    937 XZd8YaRdMGKVV1O2guQ20Dxd1RGdU18k9YfFNsj4Jtw5sTFTzHr1P0n9ybV9xCXp\n\
    938 znSxVfRg8U6TcMHoRDJR9EMKQMO4W3OQEmreEPoGt2/+kMuiHjclxLtbwDxKXTLP\n\
    939 pD0gdg3ibvlufk/ccKl/yAglDmd0dfW22oS7NgvRKUve7tzDxY1Q6O5v8BCnLFSW\n\
    940 D+z4hS1PzooYRXRkM0xYudvPkryPyu+1kEpw3fNsoQKBgQDRfXJo82XQvlX8WPdZ\n\
    941 Ts3PfBKKMVu3Wf8J3SYpuvYT816qR3ot6e4Ivv5ZCQkdDwzzBKe2jAv6JddMJIhx\n\
    942 pkGHc0KKOodd9HoBewOd8Td++hapJAGaGblhL5beIidLKjXDjLqtgoHRGlv5Cojo\n\
    943 zHa7Viel1eOPPcBumhp83oJ+mQKBgQDC6PmdETZdrW3QPm7ZXxRzF1vvpC55wmPg\n\
    944 pRfTRM059jzRzAk0QiBgVp3yk2a6Ob3mB2MLfQVDgzGf37h2oO07s5nspSFZTFnM\n\
    945 KgSjFy0xVOAVDLe+0VpbmLp1YUTYvdCNowaoTE7++5rpePUDu3BjAifx07/yaSB+\n\
    946 W+YPOfOuKwKBgQCGK6g5G5qcJSuBIaHZ6yTZvIdLRu2M8vDral5k3793a6m3uWvB\n\
    947 OFAh/eF9ONJDcD5E7zhTLEMHhXDs7YEN+QODMwjs6yuDu27gv97DK5j1lEsrLUpx\n\
    948 XgRjAE3KG2m7NF+WzO1K74khWZaKXHrvTvTEaxudlO3X8h7rN3u7ee9uEQKBgQC2\n\
    949 wI1zeTUZhsiFTlTPWfgppchdHPs6zUqq0wFQ5Zzr8Pa72+zxY+NJkU2NqinTCNsG\n\
    950 ePykQ/gQgk2gUrt595AYv2De40IuoYk9BlTMuql0LNniwsbykwd/BOgnsSlFdEy8\n\
    951 0RQn70zOhgmNSg2qDzDklJvxghLi7zE5aV9//V1/ewKBgFRHHZN1a8q/v8AAOeoB\n\
    952 ROuXfgDDpxNNUKbzLL5MO5odgZGi61PBZlxffrSOqyZoJkzawXycNtoBP47tcVzT\n\
    953 QPq5ZOB3kjHTcN7dRLmPWjji9h4O3eHCX67XaPVMSWiMuNtOZIg2an06+jxGFhLE\n\
    954 qdJNJ1DkyUc9dN2cliX4R+rG\n\
    955 -----END PRIVATE KEY-----";
    956 
    957 /* test server CA signed certificates */
    958 static const char srv_signed_cert_pem[] =
    959   "-----BEGIN CERTIFICATE-----\n\
    960 MIIFSzCCAzOgAwIBAgIBBDANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCUlUx\n\
    961 DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRswGQYDVQQKDBJ0ZXN0\n\
    962 LWxpYm1pY3JvaHR0cGQxITAfBgkqhkiG9w0BCQEWEm5vYm9keUBleGFtcGxlLm9y\n\
    963 ZzEQMA4GA1UEAwwHdGVzdC1DQTAgFw0yMjA0MjAxODQzMDJaGA8yMTIyMDMyNjE4\n\
    964 NDMwMlowZTELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwG\n\
    965 TW9zY293MRswGQYDVQQKDBJ0ZXN0LWxpYm1pY3JvaHR0cGQxFzAVBgNVBAMMDnRl\n\
    966 c3QtbWhkc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn3+2\n\
    967 psPczUhPoazjIQa8227CZblD94JnfJzimOTFIpqD5MZgF36di9IpfjD2U/2RJqNd\n\
    968 atJOido3xlAUPqGVvWoTWfYAg422JQW5aP5qk2ovi3Xizt6pOd3dM62Sl7wjurAN\n\
    969 7iqRtlkfoPrDSsX2KSBwqWp6gVOjvnXoCFf/list50NhcKeyuR1DY9btKYNEENfM\n\
    970 n+vC05yhVBxY2ZpmmqKVpIucCkgYnh4r80MusPtOB0k6IBx0Sv8v1HKCTUhu8Zx5\n\
    971 qDz6Nby0/AjESGJ0koFN8CeN1wSfNFsawtnXKGY1dl2c/l3UMAg+GawtYocpHAJo\n\
    972 kAPrGrjZu3c7TjJBswIDAQABo4HmMIHjMAsGA1UdDwQEAwIFoDAMBgNVHRMBAf8E\n\
    973 AjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMDEGA1UdEQQqMCiCDnRlc3QtbWhk\n\
    974 c2VydmVyhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBQ57Z06WJae\n\
    975 8fJIHId4QGx/HsRgDDAoBglghkgBhvhCAQ0EGxYZVGVzdCBsaWJtaWNyb2h0dHBk\n\
    976 IHNlcnZlcjARBglghkgBhvhCAQEEBAMCBkAwHwYDVR0jBBgwFoAUWHVDwKVqMcOF\n\
    977 Nd0arI3/QB3W6SwwDQYJKoZIhvcNAQELBQADggIBAI7Lggm/XzpugV93H5+KV48x\n\
    978 X+Ct8unNmPCSzCaI5hAHGeBBJpvD0KME5oiJ5p2wfCtK5Dt9zzf0S0xYdRKqU8+N\n\
    979 aKIvPoU1hFixXLwTte1qOp6TviGvA9Xn2Fc4n36dLt6e9aiqDnqPbJgBwcVO82ll\n\
    980 HJxVr3WbrAcQTB3irFUMqgAke/Cva9Bw79VZgX4ghb5EnejDzuyup4pHGzV10Myv\n\
    981 hdg+VWZbAxpCe0S4eKmstZC7mWsFCLeoRTf/9Pk1kQ6+azbTuV/9QOBNfFi8QNyb\n\
    982 18jUjmm8sc2HKo8miCGqb2sFqaGD918hfkWmR+fFkzQ3DZQrT+eYbKq2un3k0pMy\n\
    983 UySy8SRn1eadfab+GwBVb68I9TrPRMrJsIzysNXMX4iKYl2fFE/RSNnaHtPw0C8y\n\
    984 B7memyxPRl+H2xg6UjpoKYh3+8e44/XKm0rNIzXjrwA8f8gnw2TbqmMDkj1YqGnC\n\
    985 SCj5A27zUzaf2pT/YsnQXIWOJjVvbEI+YKj34wKWyTrXA093y8YI8T3mal7Kr9YM\n\
    986 WiIyPts0/aVeziM0Gunglz+8Rj1VesL52FTurobqusPgM/AME82+qb/qnxuPaCKj\n\
    987 OT1qAbIblaRuWqCsid8BzP7ZQiAnAWgMRSUg1gzDwSwRhrYQRRWAyn/Qipzec+27\n\
    988 /w0gW9EVWzFhsFeGEssi\n\
    989 -----END CERTIFICATE-----";
    990 
    991 
    992 /**
    993  * Entry point to demo.  Note: this HTTP server will make all
    994  * files in the current directory and its subdirectories available
    995  * to anyone.  Press ENTER to stop the server once it has started.
    996  *
    997  * @param argc number of arguments in argv
    998  * @param argv first and only argument should be the port number
    999  * @return 0 on success
   1000  */
   1001 int
   1002 main (int argc, char *const *argv)
   1003 {
   1004   struct MHD_Daemon *d;
   1005   unsigned int port;
   1006 
   1007   if ( (argc != 2) ||
   1008        (1 != sscanf (argv[1], "%u", &port)) ||
   1009        (UINT16_MAX < port) )
   1010   {
   1011     fprintf (stderr,
   1012              "%s PORT\n", argv[0]);
   1013     return 1;
   1014   }
   1015   #ifndef MINGW
   1016   ignore_sigpipe ();
   1017   #endif
   1018 #ifdef MHD_HAVE_LIBMAGIC
   1019   magic = magic_open (MAGIC_MIME_TYPE);
   1020   (void) magic_load (magic, NULL);
   1021 #endif /* MHD_HAVE_LIBMAGIC */
   1022 
   1023   (void) pthread_mutex_init (&mutex, NULL);
   1024   file_not_found_response =
   1025     MHD_create_response_from_buffer_static (strlen (FILE_NOT_FOUND_PAGE),
   1026                                             (const void *) FILE_NOT_FOUND_PAGE);
   1027   mark_as_html (file_not_found_response);
   1028   request_refused_response =
   1029     MHD_create_response_from_buffer_static (strlen (REQUEST_REFUSED_PAGE),
   1030                                             (const void *)
   1031                                             REQUEST_REFUSED_PAGE);
   1032   mark_as_html (request_refused_response);
   1033   internal_error_response =
   1034     MHD_create_response_from_buffer_static (strlen (INTERNAL_ERROR_PAGE),
   1035                                             (const void *) INTERNAL_ERROR_PAGE);
   1036   mark_as_html (internal_error_response);
   1037   update_directory ();
   1038   d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD
   1039                         | MHD_USE_ERROR_LOG | MHD_USE_TLS,
   1040                         (uint16_t) port,
   1041                         NULL, NULL,
   1042                         &generate_page, NULL,
   1043                         MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256
   1044                                                                       * 1024),
   1045 #ifdef PRODUCTION
   1046                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
   1047 #endif
   1048                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned
   1049                                                         int) (120 /* seconds */)
   1050                         ,
   1051                         MHD_OPTION_THREAD_POOL_SIZE, (unsigned
   1052                                                       int) NUMBER_OF_THREADS,
   1053                         MHD_OPTION_NOTIFY_COMPLETED,
   1054                         &response_completed_callback, NULL,
   1055                         /* Optionally, the gnutls_load_file() can be used to
   1056                            load the key and the certificate from file. */
   1057                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
   1058                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
   1059                         MHD_OPTION_END);
   1060   if (NULL == d)
   1061     return 1;
   1062   fprintf (stderr, "HTTP server running. Press ENTER to stop the server.\n");
   1063   (void) getc (stdin);
   1064   MHD_stop_daemon (d);
   1065   MHD_destroy_response (file_not_found_response);
   1066   MHD_destroy_response (request_refused_response);
   1067   MHD_destroy_response (internal_error_response);
   1068   update_cached_response (NULL);
   1069   (void) pthread_mutex_destroy (&mutex);
   1070 #ifdef MHD_HAVE_LIBMAGIC
   1071   magic_close (magic);
   1072 #endif /* MHD_HAVE_LIBMAGIC */
   1073   return 0;
   1074 }
   1075 
   1076 
   1077 /* end of demo_https.c */