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