Home | History | Annotate | Download | only in examples
      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2013 Christian Grothoff (and other contributing authors)
      4 
      5      This library is free software; you can redistribute it and/or
      6      modify it under the terms of the GNU Lesser General Public
      7      License as published by the Free Software Foundation; either
      8      version 2.1 of the License, or (at your option) any later version.
      9 
     10      This library is distributed in the hope that it will be useful,
     11      but WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      Lesser General Public License for more details.
     14 
     15      You should have received a copy of the GNU Lesser General Public
     16      License along with this library; if not, write to the Free Software
     17      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     18 */
     19 
     20 /**
     21  * @file demo_https.c
     22  * @brief complex demonstration site: create directory index, offer
     23  *        upload via form and HTTP POST, download with mime type detection
     24  *        and error reporting (403, etc.) --- and all of this with
     25  *        high-performance settings (large buffers, thread pool).
     26  *        If you want to benchmark MHD, this code should be used to
     27  *        run tests against.  Note that the number of threads may need
     28  *        to be adjusted depending on the number of available cores.
     29  *        Logic is identical to demo.c, just adds HTTPS support.
     30  * @author Christian Grothoff
     31  */
     32 #include "platform.h"
     33 #include <microhttpd.h>
     34 #include <unistd.h>
     35 #include <pthread.h>
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <dirent.h>
     39 #include <magic.h>
     40 #include <limits.h>
     41 #include <ctype.h>
     42 
     43 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     44 #undef CPU_COUNT
     45 #endif
     46 #if !defined(CPU_COUNT)
     47 #define CPU_COUNT 2
     48 #endif
     49 
     50 /**
     51  * Number of threads to run in the thread pool.  Should (roughly) match
     52  * the number of cores on your system.
     53  */
     54 #define NUMBER_OF_THREADS CPU_COUNT
     55 
     56 /**
     57  * How many bytes of a file do we give to libmagic to determine the mime type?
     58  * 16k might be a bit excessive, but ought not hurt performance much anyway,
     59  * and should definitively be on the safe side.
     60  */
     61 #define MAGIC_HEADER_SIZE (16 * 1024)
     62 
     63 
     64 /**
     65  * Page returned for file-not-found.
     66  */
     67 #define FILE_NOT_FOUND_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
     68 
     69 
     70 /**
     71  * Page returned for internal errors.
     72  */
     73 #define INTERNAL_ERROR_PAGE "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
     74 
     75 
     76 /**
     77  * Page returned for refused requests.
     78  */
     79 #define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
     80 
     81 
     82 /**
     83  * Head of index page.
     84  */
     85 #define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\
     86    "<h1>Upload</h1>\n"\
     87    "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\
     88    "<dl><dt>Content type:</dt><dd>"\
     89    "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\
     90    "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\
     91    "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\
     92    "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>"\
     93    "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\
     94    "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>"\
     95    "<dt>Language:</dt><dd>"\
     96    "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>"\
     97    "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\
     98    "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\
     99    "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\
    100    "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n"\
    101    "<dt>File:</dt><dd>"\
    102    "<input type=\"file\" name=\"upload\"/></dd></dl>"\
    103    "<input type=\"submit\" value=\"Send!\"/>\n"\
    104    "</form>\n"\
    105    "<h1>Download</h1>\n"\
    106    "<ol>\n"
    107 
    108 /**
    109  * Footer of index page.
    110  */
    111 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
    112 
    113 
    114 /**
    115  * NULL-terminated array of supported upload categories.  Should match HTML
    116  * in the form.
    117  */
    118 static const char * const categories[] =
    119   {
    120     "books",
    121     "images",
    122     "music",
    123     "software",
    124     "videos",
    125     "other",
    126     NULL,
    127   };
    128 
    129 
    130 /**
    131  * Specification of a supported language.
    132  */
    133 struct Language
    134 {
    135   /**
    136    * Directory name for the language.
    137    */
    138   const char *dirname;
    139 
    140   /**
    141    * Long name for humans.
    142    */
    143   const char *longname;
    144 
    145 };
    146 
    147 /**
    148  * NULL-terminated array of supported upload categories.  Should match HTML
    149  * in the form.
    150  */
    151 static const struct Language languages[] =
    152   {
    153     { "no-lang", "No language specified" },
    154     { "en", "English" },
    155     { "de", "German" },
    156     { "fr", "French" },
    157     { "es", "Spanish" },
    158     { NULL, NULL },
    159   };
    160 
    161 
    162 /**
    163  * Response returned if the requested file does not exist (or is not accessible).
    164  */
    165 static struct MHD_Response *file_not_found_response;
    166 
    167 /**
    168  * Response returned for internal errors.
    169  */
    170 static struct MHD_Response *internal_error_response;
    171 
    172 /**
    173  * Response returned for '/' (GET) to list the contents of the directory and allow upload.
    174  */
    175 static struct MHD_Response *cached_directory_response;
    176 
    177 /**
    178  * Response returned for refused uploads.
    179  */
    180 static struct MHD_Response *request_refused_response;
    181 
    182 /**
    183  * Mutex used when we update the cached directory response object.
    184  */
    185 static pthread_mutex_t mutex;
    186 
    187 /**
    188  * Global handle to MAGIC data.
    189  */
    190 static magic_t magic;
    191 
    192 
    193 /**
    194  * Mark the given response as HTML for the brower.
    195  *
    196  * @param response response to mark
    197  */
    198 static void
    199 mark_as_html (struct MHD_Response *response)
    200 {
    201   (void) MHD_add_response_header (response,
    202 				  MHD_HTTP_HEADER_CONTENT_TYPE,
    203 				  "text/html");
    204 }
    205 
    206 
    207 /**
    208  * Replace the existing 'cached_directory_response' with the
    209  * given response.
    210  *
    211  * @param response new directory response
    212  */
    213 static void
    214 update_cached_response (struct MHD_Response *response)
    215 {
    216   (void) pthread_mutex_lock (&mutex);
    217   if (NULL != cached_directory_response)
    218     MHD_destroy_response (cached_directory_response);
    219   cached_directory_response = response;
    220   (void) pthread_mutex_unlock (&mutex);
    221 }
    222 
    223 
    224 /**
    225  * Context keeping the data for the response we're building.
    226  */
    227 struct ResponseDataContext
    228 {
    229   /**
    230    * Response data string.
    231    */
    232   char *buf;
    233 
    234   /**
    235    * Number of bytes allocated for 'buf'.
    236    */
    237   size_t buf_len;
    238 
    239   /**
    240    * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
    241    */
    242   size_t off;
    243 
    244 };
    245 
    246 
    247 /**
    248  * Create a listing of the files in 'dirname' in HTML.
    249  *
    250  * @param rdc where to store the list of files
    251  * @param dirname name of the directory to list
    252  * @return MHD_YES on success, MHD_NO on error
    253  */
    254 static int
    255 list_directory (struct ResponseDataContext *rdc,
    256 		const char *dirname)
    257 {
    258   char fullname[PATH_MAX];
    259   struct stat sbuf;
    260   DIR *dir;
    261   struct dirent *de;
    262 
    263   if (NULL == (dir = opendir (dirname)))
    264     return MHD_NO;
    265   while (NULL != (de = readdir (dir)))
    266     {
    267       if ('.' == de->d_name[0])
    268 	continue;
    269       if (sizeof (fullname) <= (size_t)
    270 	  snprintf (fullname, sizeof (fullname),
    271 		    "%s/%s",
    272 		    dirname, de->d_name))
    273 	continue; /* ugh, file too long? how can this be!? */
    274       if (0 != stat (fullname, &sbuf))
    275 	continue; /* ugh, failed to 'stat' */
    276       if (! S_ISREG (sbuf.st_mode))
    277 	continue; /* not a regular file, skip */
    278       if (rdc->off + 1024 > rdc->buf_len)
    279 	{
    280 	  void *r;
    281 
    282 	  if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
    283 	    break; /* more than SIZE_T _index_ size? Too big for us */
    284 	  rdc->buf_len = 2 * rdc->buf_len + 1024;
    285 	  if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
    286 	    break; /* out of memory */
    287 	  rdc->buf = r;
    288 	}
    289       rdc->off += snprintf (&rdc->buf[rdc->off],
    290 			    rdc->buf_len - rdc->off,
    291 			    "<li><a href=\"/%s\">%s</a></li>\n",
    292 			    fullname,
    293 			    de->d_name);
    294     }
    295   (void) closedir (dir);
    296   return MHD_YES;
    297 }
    298 
    299 
    300 /**
    301  * Re-scan our local directory and re-build the index.
    302  */
    303 static void
    304 update_directory ()
    305 {
    306   static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
    307   struct MHD_Response *response;
    308   struct ResponseDataContext rdc;
    309   unsigned int language_idx;
    310   unsigned int category_idx;
    311   const struct Language *language;
    312   const char *category;
    313   char dir_name[128];
    314   struct stat sbuf;
    315 
    316   rdc.buf_len = initial_allocation;
    317   if (NULL == (rdc.buf = malloc (rdc.buf_len)))
    318     {
    319       update_cached_response (NULL);
    320       return;
    321     }
    322   rdc.off = snprintf (rdc.buf, rdc.buf_len,
    323 		      "%s",
    324 		      INDEX_PAGE_HEADER);
    325   for (language_idx = 0; NULL != languages[language_idx].dirname; language_idx++)
    326     {
    327       language = &languages[language_idx];
    328 
    329       if (0 != stat (language->dirname, &sbuf))
    330 	continue; /* empty */
    331       /* we ensured always +1k room, filenames are ~256 bytes,
    332 	 so there is always still enough space for the header
    333 	 without need for an additional reallocation check. */
    334       rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    335 			   "<h2>%s</h2>\n",
    336 			   language->longname);
    337       for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
    338 	{
    339 	  category = categories[category_idx];
    340 	  snprintf (dir_name, sizeof (dir_name),
    341 		    "%s/%s",
    342 		    language->dirname,
    343 		    category);
    344 	  if (0 != stat (dir_name, &sbuf))
    345 	    continue; /* empty */
    346 
    347 	  /* we ensured always +1k room, filenames are ~256 bytes,
    348 	     so there is always still enough space for the header
    349 	     without need for an additional reallocation check. */
    350 	  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    351 			       "<h3>%s</h3>\n",
    352 			       category);
    353 
    354 	  if (MHD_NO == list_directory (&rdc, dir_name))
    355 	    {
    356 	      free (rdc.buf);
    357 	      update_cached_response (NULL);
    358 	      return;
    359 	    }
    360 	}
    361     }
    362   /* we ensured always +1k room, filenames are ~256 bytes,
    363      so there is always still enough space for the footer
    364      without need for a final reallocation check. */
    365   rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    366 		       "%s",
    367 		       INDEX_PAGE_FOOTER);
    368   initial_allocation = rdc.buf_len; /* remember for next time */
    369   response = MHD_create_response_from_buffer (rdc.off,
    370 					      rdc.buf,
    371 					      MHD_RESPMEM_MUST_FREE);
    372   mark_as_html (response);
    373 #if FORCE_CLOSE
    374   (void) MHD_add_response_header (response,
    375 				  MHD_HTTP_HEADER_CONNECTION,
    376 				  "close");
    377 #endif
    378   update_cached_response (response);
    379 }
    380 
    381 
    382 /**
    383  * Context we keep for an upload.
    384  */
    385 struct UploadContext
    386 {
    387   /**
    388    * Handle where we write the uploaded file to.
    389    */
    390   int fd;
    391 
    392   /**
    393    * Name of the file on disk (used to remove on errors).
    394    */
    395   char *filename;
    396 
    397   /**
    398    * Language for the upload.
    399    */
    400   char *language;
    401 
    402   /**
    403    * Category for the upload.
    404    */
    405   char *category;
    406 
    407   /**
    408    * Post processor we're using to process the upload.
    409    */
    410   struct MHD_PostProcessor *pp;
    411 
    412   /**
    413    * Handle to connection that we're processing the upload for.
    414    */
    415   struct MHD_Connection *connection;
    416 
    417   /**
    418    * Response to generate, NULL to use directory.
    419    */
    420   struct MHD_Response *response;
    421 };
    422 
    423 
    424 /**
    425  * Append the 'size' bytes from 'data' to '*ret', adding
    426  * 0-termination.  If '*ret' is NULL, allocate an empty string first.
    427  *
    428  * @param ret string to update, NULL or 0-terminated
    429  * @param data data to append
    430  * @param size number of bytes in 'data'
    431  * @return MHD_NO on allocation failure, MHD_YES on success
    432  */
    433 static int
    434 do_append (char **ret,
    435 	   const char *data,
    436 	   size_t size)
    437 {
    438   char *buf;
    439   size_t old_len;
    440 
    441   if (NULL == *ret)
    442     old_len = 0;
    443   else
    444     old_len = strlen (*ret);
    445   buf = malloc (old_len + size + 1);
    446   if (NULL == buf)
    447     return MHD_NO;
    448   memcpy (buf, *ret, old_len);
    449   if (NULL != *ret)
    450     free (*ret);
    451   memcpy (&buf[old_len], data, size);
    452   buf[old_len + size] = '\0';
    453   *ret = buf;
    454   return MHD_YES;
    455 }
    456 
    457 
    458 /**
    459  * Iterator over key-value pairs where the value
    460  * maybe made available in increments and/or may
    461  * not be zero-terminated.  Used for processing
    462  * POST data.
    463  *
    464  * @param cls user-specified closure
    465  * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
    466  * @param key 0-terminated key for the value
    467  * @param filename name of the uploaded file, NULL if not known
    468  * @param content_type mime-type of the data, NULL if not known
    469  * @param transfer_encoding encoding of the data, NULL if not known
    470  * @param data pointer to size bytes of data at the
    471  *              specified offset
    472  * @param off offset of data in the overall value
    473  * @param size number of bytes in data available
    474  * @return MHD_YES to continue iterating,
    475  *         MHD_NO to abort the iteration
    476  */
    477 static int
    478 process_upload_data (void *cls,
    479 		     enum MHD_ValueKind kind,
    480 		     const char *key,
    481 		     const char *filename,
    482 		     const char *content_type,
    483 		     const char *transfer_encoding,
    484 		     const char *data,
    485 		     uint64_t off,
    486 		     size_t size)
    487 {
    488   struct UploadContext *uc = cls;
    489   int i;
    490 
    491   if (0 == strcmp (key, "category"))
    492     return do_append (&uc->category, data, size);
    493   if (0 == strcmp (key, "language"))
    494     return do_append (&uc->language, data, size);
    495   if (0 != strcmp (key, "upload"))
    496     {
    497       fprintf (stderr,
    498 	       "Ignoring unexpected form value `%s'\n",
    499 	       key);
    500       return MHD_YES; /* ignore */
    501     }
    502   if (NULL == filename)
    503     {
    504       fprintf (stderr, "No filename, aborting upload\n");
    505       return MHD_NO; /* no filename, error */
    506     }
    507   if ( (NULL == uc->category) ||
    508        (NULL == uc->language) )
    509     {
    510       fprintf (stderr,
    511 	       "Missing form data for upload `%s'\n",
    512 	       filename);
    513       uc->response = request_refused_response;
    514       return MHD_NO;
    515     }
    516   if (-1 == uc->fd)
    517     {
    518       char fn[PATH_MAX];
    519 
    520       if ( (NULL != strstr (filename, "..")) ||
    521 	   (NULL != strchr (filename, '/')) ||
    522 	   (NULL != strchr (filename, '\\')) )
    523 	{
    524 	  uc->response = request_refused_response;
    525 	  return MHD_NO;
    526 	}
    527       /* create directories -- if they don't exist already */
    528 #ifdef WINDOWS
    529       (void) mkdir (uc->language);
    530 #else
    531       (void) mkdir (uc->language, S_IRWXU);
    532 #endif
    533       snprintf (fn, sizeof (fn),
    534 		"%s/%s",
    535 		uc->language,
    536 		uc->category);
    537 #ifdef WINDOWS
    538       (void) mkdir (fn);
    539 #else
    540       (void) mkdir (fn, S_IRWXU);
    541 #endif
    542       /* open file */
    543       snprintf (fn, sizeof (fn),
    544 		"%s/%s/%s",
    545 		uc->language,
    546 		uc->category,
    547 		filename);
    548       for (i=strlen (fn)-1;i>=0;i--)
    549 	if (! isprint ((int) fn[i]))
    550 	  fn[i] = '_';
    551       uc->fd = open (fn,
    552 		     O_CREAT | O_EXCL
    553 #if O_LARGEFILE
    554 		     | O_LARGEFILE
    555 #endif
    556 		     | O_WRONLY,
    557 		     S_IRUSR | S_IWUSR);
    558       if (-1 == uc->fd)
    559 	{
    560 	  fprintf (stderr,
    561 		   "Error opening file `%s' for upload: %s\n",
    562 		   fn,
    563 		   strerror (errno));
    564 	  uc->response = request_refused_response;
    565 	  return MHD_NO;
    566 	}
    567       uc->filename = strdup (fn);
    568     }
    569   if ( (0 != size) &&
    570        (size != (size_t) write (uc->fd, data, size)) )
    571     {
    572       /* write failed; likely: disk full */
    573       fprintf (stderr,
    574 	       "Error writing to file `%s': %s\n",
    575 	       uc->filename,
    576 	       strerror (errno));
    577       uc->response = internal_error_response;
    578       close (uc->fd);
    579       uc->fd = -1;
    580       if (NULL != uc->filename)
    581 	{
    582 	  unlink (uc->filename);
    583 	  free (uc->filename);
    584 	  uc->filename = NULL;
    585 	}
    586       return MHD_NO;
    587     }
    588   return MHD_YES;
    589 }
    590 
    591 
    592 /**
    593  * Function called whenever a request was completed.
    594  * Used to clean up 'struct UploadContext' objects.
    595  *
    596  * @param cls client-defined closure, NULL
    597  * @param connection connection handle
    598  * @param con_cls value as set by the last call to
    599  *        the MHD_AccessHandlerCallback, points to NULL if this was
    600  *            not an upload
    601  * @param toe reason for request termination
    602  */
    603 static void
    604 response_completed_callback (void *cls,
    605 			     struct MHD_Connection *connection,
    606 			     void **con_cls,
    607 			     enum MHD_RequestTerminationCode toe)
    608 {
    609   struct UploadContext *uc = *con_cls;
    610 
    611   if (NULL == uc)
    612     return; /* this request wasn't an upload request */
    613   if (NULL != uc->pp)
    614     {
    615       MHD_destroy_post_processor (uc->pp);
    616       uc->pp = NULL;
    617     }
    618   if (-1 != uc->fd)
    619   {
    620     (void) close (uc->fd);
    621     if (NULL != uc->filename)
    622       {
    623 	fprintf (stderr,
    624 		 "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
    625 		 uc->filename);
    626 	(void) unlink (uc->filename);
    627       }
    628   }
    629   if (NULL != uc->filename)
    630     free (uc->filename);
    631   free (uc);
    632 }
    633 
    634 
    635 /**
    636  * Return the current directory listing.
    637  *
    638  * @param connection connection to return the directory for
    639  * @return MHD_YES on success, MHD_NO on error
    640  */
    641 static int
    642 return_directory_response (struct MHD_Connection *connection)
    643 {
    644   int ret;
    645 
    646   (void) pthread_mutex_lock (&mutex);
    647   if (NULL == cached_directory_response)
    648     ret = MHD_queue_response (connection,
    649 			      MHD_HTTP_INTERNAL_SERVER_ERROR,
    650 			      internal_error_response);
    651   else
    652     ret = MHD_queue_response (connection,
    653 			      MHD_HTTP_OK,
    654 			      cached_directory_response);
    655   (void) pthread_mutex_unlock (&mutex);
    656   return ret;
    657 }
    658 
    659 
    660 /**
    661  * Main callback from MHD, used to generate the page.
    662  *
    663  * @param cls NULL
    664  * @param connection connection handle
    665  * @param url requested URL
    666  * @param method GET, PUT, POST, etc.
    667  * @param version HTTP version
    668  * @param upload_data data from upload (PUT/POST)
    669  * @param upload_data_size number of bytes in "upload_data"
    670  * @param ptr our context
    671  * @return MHD_YES on success, MHD_NO to drop connection
    672  */
    673 static int
    674 generate_page (void *cls,
    675 	       struct MHD_Connection *connection,
    676 	       const char *url,
    677 	       const char *method,
    678 	       const char *version,
    679 	       const char *upload_data,
    680 	       size_t *upload_data_size, void **ptr)
    681 {
    682   struct MHD_Response *response;
    683   int ret;
    684   int fd;
    685   struct stat buf;
    686 
    687   if (0 != strcmp (url, "/"))
    688     {
    689       /* should be file download */
    690       char file_data[MAGIC_HEADER_SIZE];
    691       ssize_t got;
    692       const char *mime;
    693 
    694       if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    695 	return MHD_NO;  /* unexpected method (we're not polite...) */
    696       if ( (0 == stat (&url[1], &buf)) &&
    697 	   (NULL == strstr (&url[1], "..")) &&
    698 	   ('/' != url[1]))
    699 	fd = open (&url[1], O_RDONLY);
    700       else
    701 	fd = -1;
    702       if (-1 == fd)
    703 	return MHD_queue_response (connection,
    704 				   MHD_HTTP_NOT_FOUND,
    705 				   file_not_found_response);
    706       /* read beginning of the file to determine mime type  */
    707       got = read (fd, file_data, sizeof (file_data));
    708       if (-1 != got)
    709 	mime = magic_buffer (magic, file_data, got);
    710       else
    711 	mime = NULL;
    712       (void) lseek (fd, 0, SEEK_SET);
    713 
    714       if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
    715 							   fd)))
    716 	{
    717 	  /* internal error (i.e. out of memory) */
    718 	  (void) close (fd);
    719 	  return MHD_NO;
    720 	}
    721 
    722       /* add mime type if we had one */
    723       if (NULL != mime)
    724 	(void) MHD_add_response_header (response,
    725 					MHD_HTTP_HEADER_CONTENT_TYPE,
    726 					mime);
    727       ret = MHD_queue_response (connection,
    728 				MHD_HTTP_OK,
    729 				response);
    730       MHD_destroy_response (response);
    731       return ret;
    732     }
    733 
    734   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    735     {
    736       /* upload! */
    737       struct UploadContext *uc = *ptr;
    738 
    739       if (NULL == uc)
    740 	{
    741 	  if (NULL == (uc = malloc (sizeof (struct UploadContext))))
    742 	    return MHD_NO; /* out of memory, close connection */
    743 	  memset (uc, 0, sizeof (struct UploadContext));
    744           uc->fd = -1;
    745 	  uc->connection = connection;
    746 	  uc->pp = MHD_create_post_processor (connection,
    747 					      64 * 1024 /* buffer size */,
    748 					      &process_upload_data, uc);
    749 	  if (NULL == uc->pp)
    750 	    {
    751 	      /* out of memory, close connection */
    752 	      free (uc);
    753 	      return MHD_NO;
    754 	    }
    755 	  *ptr = uc;
    756 	  return MHD_YES;
    757 	}
    758       if (0 != *upload_data_size)
    759 	{
    760 	  if (NULL == uc->response)
    761 	    (void) MHD_post_process (uc->pp,
    762 				     upload_data,
    763 				     *upload_data_size);
    764 	  *upload_data_size = 0;
    765 	  return MHD_YES;
    766 	}
    767       /* end of upload, finish it! */
    768       MHD_destroy_post_processor (uc->pp);
    769       uc->pp = NULL;
    770       if (-1 != uc->fd)
    771 	{
    772 	  close (uc->fd);
    773 	  uc->fd = -1;
    774 	}
    775       if (NULL != uc->response)
    776 	{
    777 	  return MHD_queue_response (connection,
    778 				     MHD_HTTP_FORBIDDEN,
    779 				     uc->response);
    780 	}
    781       else
    782 	{
    783 	  update_directory ();
    784 	  return return_directory_response (connection);
    785 	}
    786     }
    787   if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
    788   {
    789     return return_directory_response (connection);
    790   }
    791 
    792   /* unexpected request, refuse */
    793   return MHD_queue_response (connection,
    794 			     MHD_HTTP_FORBIDDEN,
    795 			     request_refused_response);
    796 }
    797 
    798 
    799 /**
    800  * Function called if we get a SIGPIPE. Does nothing.
    801  *
    802  * @param sig will be SIGPIPE (ignored)
    803  */
    804 static void
    805 catcher (int sig)
    806 {
    807   /* do nothing */
    808 }
    809 
    810 
    811 /**
    812  * setup handlers to ignore SIGPIPE.
    813  */
    814 #ifndef MINGW
    815 static void
    816 ignore_sigpipe ()
    817 {
    818   struct sigaction oldsig;
    819   struct sigaction sig;
    820 
    821   sig.sa_handler = &catcher;
    822   sigemptyset (&sig.sa_mask);
    823 #ifdef SA_INTERRUPT
    824   sig.sa_flags = SA_INTERRUPT;  /* SunOS */
    825 #else
    826   sig.sa_flags = SA_RESTART;
    827 #endif
    828   if (0 != sigaction (SIGPIPE, &sig, &oldsig))
    829     fprintf (stderr,
    830              "Failed to install SIGPIPE handler: %s\n", strerror (errno));
    831 }
    832 #endif
    833 
    834 /* test server key */
    835 const char srv_signed_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n"
    836   "MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW\n"
    837   "+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL\n"
    838   "q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0\n"
    839   "20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6\n"
    840   "QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x\n"
    841   "yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4\n"
    842   "+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K\n"
    843   "lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer\n"
    844   "DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM\n"
    845   "bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP\n"
    846   "sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ\n"
    847   "Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN\n"
    848   "d+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU\n"
    849   "pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1\n"
    850   "b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0\n"
    851   "cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T\n"
    852   "LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt\n"
    853   "2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92\n"
    854   "SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH\n"
    855   "Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB\n"
    856   "4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7\n"
    857   "IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q\n"
    858   "C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R\n"
    859   "GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv\n"
    860   "tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O\n"
    861   "-----END RSA PRIVATE KEY-----\n";
    862 
    863 /* test server CA signed certificates */
    864 const char srv_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n"
    865   "MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n"
    866   "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n"
    867   "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n"
    868   "vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn\n"
    869   "dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ\n"
    870   "F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC\n"
    871   "IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB\n"
    872   "II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9\n"
    873   "RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n"
    874   "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d\n"
    875   "XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG\n"
    876   "CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz\n"
    877   "GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg\n"
    878   "A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt\n"
    879   "YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo\n"
    880   "Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6\n"
    881   "4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q==\n"
    882   "-----END CERTIFICATE-----\n";
    883 
    884 
    885 /**
    886  * Entry point to demo.  Note: this HTTP server will make all
    887  * files in the current directory and its subdirectories available
    888  * to anyone.  Press ENTER to stop the server once it has started.
    889  *
    890  * @param argc number of arguments in argv
    891  * @param argv first and only argument should be the port number
    892  * @return 0 on success
    893  */
    894 int
    895 main (int argc, char *const *argv)
    896 {
    897   struct MHD_Daemon *d;
    898   unsigned int port;
    899 
    900   if ( (argc != 2) ||
    901        (1 != sscanf (argv[1], "%u", &port)) ||
    902        (UINT16_MAX < port) )
    903     {
    904       fprintf (stderr,
    905 	       "%s PORT\n", argv[0]);
    906       return 1;
    907     }
    908   #ifndef MINGW
    909   ignore_sigpipe ();
    910   #endif
    911   magic = magic_open (MAGIC_MIME_TYPE);
    912   (void) magic_load (magic, NULL);
    913 
    914   (void) pthread_mutex_init (&mutex, NULL);
    915   file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE),
    916 							     (void *) FILE_NOT_FOUND_PAGE,
    917 							     MHD_RESPMEM_PERSISTENT);
    918   mark_as_html (file_not_found_response);
    919   request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE),
    920 							     (void *) REQUEST_REFUSED_PAGE,
    921 							     MHD_RESPMEM_PERSISTENT);
    922   mark_as_html (request_refused_response);
    923   internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE),
    924 							     (void *) INTERNAL_ERROR_PAGE,
    925 							     MHD_RESPMEM_PERSISTENT);
    926   mark_as_html (internal_error_response);
    927   update_directory ();
    928   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_SSL
    929 #if EPOLL_SUPPORT
    930 			| MHD_USE_EPOLL_LINUX_ONLY
    931 #endif
    932 			,
    933                         port,
    934                         NULL, NULL,
    935 			&generate_page, NULL,
    936 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
    937 #if PRODUCTION
    938 			MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
    939 #endif
    940 			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */),
    941 			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
    942 			MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL,
    943                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
    944                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
    945 			MHD_OPTION_END);
    946   if (NULL == d)
    947     return 1;
    948   fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
    949   (void) getc (stdin);
    950   MHD_stop_daemon (d);
    951   MHD_destroy_response (file_not_found_response);
    952   MHD_destroy_response (request_refused_response);
    953   MHD_destroy_response (internal_error_response);
    954   update_cached_response (NULL);
    955   (void) pthread_mutex_destroy (&mutex);
    956   magic_close (magic);
    957   return 0;
    958 }
    959 
    960 /* end of demo_https.c */
    961