fileserver_example_external_select.c (5913B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2008 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /** 21 * @file fileserver_example_external_select.c 22 * @brief minimal example for how to use libmicrohttpd to server files 23 * @author Christian Grothoff 24 * @author Karlson2k (Evgeny Grin) 25 */ 26 27 #include "platform.h" 28 #include <microhttpd.h> 29 #include <sys/stat.h> 30 #include <unistd.h> 31 #include <errno.h> 32 33 #define PAGE \ 34 "<html><head><title>File not found</title></head><body>File not found</body></html>" 35 36 static ssize_t 37 file_reader (void *cls, uint64_t pos, char *buf, size_t max) 38 { 39 FILE *file = (FILE *) cls; 40 size_t bytes_read; 41 42 /* 'fseek' may not support files larger 2GiB, depending on platform. 43 * For production code, make sure that 'pos' has valid values, supported by 44 * 'fseek', or use 'fseeko' or similar function. */ 45 if (0 != fseek (file, (long) pos, SEEK_SET)) 46 return MHD_CONTENT_READER_END_WITH_ERROR; 47 bytes_read = fread (buf, 1, max, file); 48 if (0 == bytes_read) 49 return (0 != ferror (file)) ? MHD_CONTENT_READER_END_WITH_ERROR : 50 MHD_CONTENT_READER_END_OF_STREAM; 51 return (ssize_t) bytes_read; 52 } 53 54 55 static void 56 free_callback (void *cls) 57 { 58 FILE *file = cls; 59 fclose (file); 60 } 61 62 63 static enum MHD_Result 64 ahc_echo (void *cls, 65 struct MHD_Connection *connection, 66 const char *url, 67 const char *method, 68 const char *version, 69 const char *upload_data, 70 size_t *upload_data_size, void **req_cls) 71 { 72 static int aptr; 73 struct MHD_Response *response; 74 enum MHD_Result ret; 75 FILE *file; 76 int fd; 77 struct stat buf; 78 (void) cls; /* Unused. Silent compiler warning. */ 79 (void) version; /* Unused. Silent compiler warning. */ 80 (void) upload_data; /* Unused. Silent compiler warning. */ 81 (void) upload_data_size; /* Unused. Silent compiler warning. */ 82 83 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 84 return MHD_NO; /* unexpected method */ 85 if (&aptr != *req_cls) 86 { 87 /* do never respond on first call */ 88 *req_cls = &aptr; 89 return MHD_YES; 90 } 91 *req_cls = NULL; /* reset when done */ 92 93 file = fopen (&url[1], "rb"); 94 if (NULL != file) 95 { 96 fd = fileno (file); 97 if (-1 == fd) 98 { 99 (void) fclose (file); 100 return MHD_NO; /* internal error */ 101 } 102 if ( (0 != fstat (fd, &buf)) || 103 (! S_ISREG (buf.st_mode)) ) 104 { 105 /* not a regular file, refuse to serve */ 106 fclose (file); 107 file = NULL; 108 } 109 } 110 111 if (NULL == file) 112 { 113 response = MHD_create_response_from_buffer_static (strlen (PAGE), 114 PAGE); 115 ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response); 116 MHD_destroy_response (response); 117 } 118 else 119 { 120 response = MHD_create_response_from_callback ((size_t) buf.st_size, 121 32 * 1024, /* 32k page size */ 122 &file_reader, 123 file, 124 &free_callback); 125 if (NULL == response) 126 { 127 fclose (file); 128 return MHD_NO; 129 } 130 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 131 MHD_destroy_response (response); 132 } 133 return ret; 134 } 135 136 137 int 138 main (int argc, char *const *argv) 139 { 140 struct MHD_Daemon *d; 141 time_t end; 142 time_t t; 143 struct timeval tv; 144 fd_set rs; 145 fd_set ws; 146 fd_set es; 147 MHD_socket max; 148 uint64_t mhd_timeout; 149 int port; 150 151 if (argc != 3) 152 { 153 printf ("%s PORT SECONDS-TO-RUN\n", argv[0]); 154 return 1; 155 } 156 port = atoi (argv[1]); 157 if ( (1 > port) || (port > 65535) ) 158 { 159 fprintf (stderr, 160 "Port must be a number between 1 and 65535.\n"); 161 return 1; 162 } 163 164 d = MHD_start_daemon (MHD_USE_ERROR_LOG, 165 (uint16_t) port, 166 NULL, NULL, &ahc_echo, NULL, 167 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 168 MHD_OPTION_END); 169 if (d == NULL) 170 return 1; 171 end = time (NULL) + atoi (argv[2]); 172 while ((t = time (NULL)) < end) 173 { 174 #if ! defined(_WIN32) || defined(__CYGWIN__) 175 tv.tv_sec = end - t; 176 #else /* Native W32 */ 177 tv.tv_sec = (long) (end - t); 178 #endif /* Native W32 */ 179 tv.tv_usec = 0; 180 max = 0; 181 FD_ZERO (&rs); 182 FD_ZERO (&ws); 183 FD_ZERO (&es); 184 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) 185 break; /* fatal internal error */ 186 if (MHD_get_timeout64 (d, &mhd_timeout) == MHD_YES) 187 { 188 if (((uint64_t) tv.tv_sec) < mhd_timeout / 1000LL) 189 { 190 #if ! defined(_WIN32) || defined(__CYGWIN__) 191 tv.tv_sec = (time_t) (mhd_timeout / 1000LL); 192 #else /* Native W32 */ 193 tv.tv_sec = (long) (mhd_timeout / 1000LL); 194 #endif /* Native W32 */ 195 tv.tv_usec = ((long) (mhd_timeout % 1000)) * 1000; 196 } 197 } 198 if (-1 == select ((int) max + 1, &rs, &ws, &es, &tv)) 199 { 200 if (EINTR != errno) 201 abort (); 202 } 203 MHD_run (d); 204 } 205 MHD_stop_daemon (d); 206 return 0; 207 }