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