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 */
117 static 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 */
132 struct 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 */
150 static 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 */
164 static struct MHD_Response *file_not_found_response;
165
166 /**
167 * Response returned for internal errors.
168 */
169 static struct MHD_Response *internal_error_response;
170
171 /**
172 * Response returned for '/' (GET) to list the contents of the directory and allow upload.
173 */
174 static struct MHD_Response *cached_directory_response;
175
176 /**
177 * Response returned for refused uploads.
178 */
179 static struct MHD_Response *request_refused_response;
180
181 /**
182 * Mutex used when we update the cached directory response object.
183 */
184 static pthread_mutex_t mutex;
185
186 /**
187 * Global handle to MAGIC data.
188 */
189 static magic_t magic;
190
191
192 /**
193 * Mark the given response as HTML for the brower.
194 *
195 * @param response response to mark
196 */
197 static void
mark_as_html(struct MHD_Response * response)198 mark_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 */
212 static void
update_cached_response(struct MHD_Response * response)213 update_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 */
226 struct 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 */
253 static int
list_directory(struct ResponseDataContext * rdc,const char * dirname)254 list_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 */
302 static void
update_directory()303 update_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 */
384 struct 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 */
432 static int
do_append(char ** ret,const char * data,size_t size)433 do_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 */
476 static int
process_upload_data(void * cls,enum MHD_ValueKind kind,const char * key,const char * filename,const char * content_type,const char * transfer_encoding,const char * data,uint64_t off,size_t size)477 process_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 */
602 static void
response_completed_callback(void * cls,struct MHD_Connection * connection,void ** con_cls,enum MHD_RequestTerminationCode toe)603 response_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 */
640 static int
return_directory_response(struct MHD_Connection * connection)641 return_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 */
672 static int
generate_page(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** ptr)673 generate_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 */
803 static void
catcher(int sig)804 catcher (int sig)
805 {
806 /* do nothing */
807 }
808
809
810 /**
811 * setup handlers to ignore SIGPIPE.
812 */
813 #ifndef MINGW
814 static void
ignore_sigpipe()815 ignore_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 */
843 int
main(int argc,char * const * argv)844 main (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